This commit is contained in:
che
2026-07-25 10:31:32 +05:00
parent 42029533fd
commit 860ba21dd0
15 changed files with 1362 additions and 230 deletions
+102 -5
View File
@@ -18,6 +18,12 @@ struct ContractPage {
results: Vec<RemoteContract>,
}
#[derive(Debug, Deserialize)]
struct ContractCategoryPage {
links: PageLinks,
results: Vec<RemoteContractCategory>,
}
#[derive(Debug, Deserialize)]
struct PageLinks {
next: Option<String>,
@@ -34,6 +40,8 @@ struct RemoteContract {
contract_type: String,
#[serde(rename = "get_category")]
category: String,
#[serde(default)]
category_id: Option<i64>,
#[serde(rename = "get_amount_total")]
amount_total_display: String,
#[serde(rename = "get_amount_by_ds")]
@@ -62,6 +70,17 @@ struct RemoteContract {
get_employee: Option<RemoteEmployee>,
}
#[derive(Debug, Deserialize)]
struct RemoteContractCategory {
id: i64,
name: String,
name_group: String,
template: Option<String>,
is_questionnair: bool,
parent: Option<i64>,
secure_group: Option<i64>,
}
#[derive(Debug, Deserialize)]
struct RemoteBillCost {
#[serde(rename = "cost__sum")]
@@ -151,6 +170,8 @@ pub async fn sync_contracts(
.as_ref()
.ok_or_else(|| ApiError::bad_request("sync requires [remote].base_url config"))?;
let client = reqwest::Client::new();
sync_contract_categories(api, &client, &token, &remote.base_url).await?;
let mut next_url = Some(format!(
"{}/api/contract/?page_size=100",
remote.base_url.trim_end_matches('/')
@@ -182,12 +203,14 @@ pub async fn sync_contracts(
for contract in &page.results {
upsert_contract_dependencies(api, contract).await?;
upsert_contract(api, contract).await?;
let detail = fetch_contract_detail(&client, &token, &remote.base_url, contract.id).await?;
let detail =
fetch_contract_detail(&client, &token, &remote.base_url, contract.id).await?;
for application in &detail.contractapplicationfile_set {
let local_path =
download_application_file(&client, &token, &app_data_dir, application).await?;
upsert_contract_application_file(api, contract.id, application, &local_path).await?;
upsert_contract_application_file(api, contract.id, application, &local_path)
.await?;
applications += 1;
if !local_path.is_empty() {
files_downloaded += 1;
@@ -208,6 +231,74 @@ pub async fn sync_contracts(
})
}
async fn sync_contract_categories(
api: &TauriApi,
client: &reqwest::Client,
token: &str,
base_url: &str,
) -> Result<(), ApiError> {
let mut next_url = Some(format!(
"{}/api/cont/category/?page_size=100",
base_url.trim_end_matches('/')
));
while let Some(url) = next_url {
let response = client
.get(url)
.header(reqwest::header::AUTHORIZATION, format!("Token {token}"))
.send()
.await?;
if !response.status().is_success() {
let status = response.status();
let detail = response.text().await.unwrap_or_default();
return Err(ApiError::new(
"remote_error",
format!("remote category request failed with {status}: {detail}"),
));
}
let page = response.json::<ContractCategoryPage>().await?;
for category in &page.results {
upsert_contract_category(api, category).await?;
}
next_url = page.links.next;
}
Ok(())
}
async fn upsert_contract_category(
api: &TauriApi,
category: &RemoteContractCategory,
) -> Result<(), ApiError> {
sqlx::query(
"INSERT INTO contract_category (
id, name, name_group, template, is_questionnair, parent, secure_group
)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
ON CONFLICT(id) DO UPDATE SET
name = excluded.name,
name_group = excluded.name_group,
template = excluded.template,
is_questionnair = excluded.is_questionnair,
parent = excluded.parent,
secure_group = excluded.secure_group",
)
.bind(category.id)
.bind(&category.name)
.bind(&category.name_group)
.bind(category.template.as_deref())
.bind(category.is_questionnair)
.bind(category.parent)
.bind(category.secure_group)
.execute(api.state().db().pool())
.await
.map_err(database_error)?;
Ok(())
}
async fn fetch_contract_detail(
client: &reqwest::Client,
token: &str,
@@ -250,7 +341,11 @@ async fn download_application_file(
.await
.map_err(file_error)?;
let file_name = format!("{}_{}", application.id, sanitize_file_name(&application.scan_name));
let file_name = format!(
"{}_{}",
application.id,
sanitize_file_name(&application.scan_name)
);
let path = directory.join(file_name);
let response = client
@@ -379,7 +474,7 @@ async fn upsert_contract_dependencies(
async fn upsert_contract(api: &TauriApi, contract: &RemoteContract) -> Result<(), ApiError> {
sqlx::query(
"INSERT INTO contractapp (
id, name, number, absolute_url, contract_type, category, amount_total_display,
id, name, number, absolute_url, contract_type, category, category_id, amount_total_display,
amount_by_ds, comment, date, status_name, status, nds, name_of_product,
counterparty_name, amount, month_pay, avans_pay, amount_total, estimate_nds_cost,
estimate_nds_cert, bill_cost_sum, bill_paid_sum, income_total, arrears,
@@ -387,7 +482,7 @@ async fn upsert_contract(api: &TauriApi, contract: &RemoteContract) -> Result<()
)
VALUES (
?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15,
?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29
?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28, ?29, ?30
)
ON CONFLICT(id) DO UPDATE SET
name = excluded.name,
@@ -395,6 +490,7 @@ async fn upsert_contract(api: &TauriApi, contract: &RemoteContract) -> Result<()
absolute_url = excluded.absolute_url,
contract_type = excluded.contract_type,
category = excluded.category,
category_id = excluded.category_id,
amount_total_display = excluded.amount_total_display,
amount_by_ds = excluded.amount_by_ds,
comment = excluded.comment,
@@ -425,6 +521,7 @@ async fn upsert_contract(api: &TauriApi, contract: &RemoteContract) -> Result<()
.bind(&contract.absolute_url)
.bind(&contract.contract_type)
.bind(&contract.category)
.bind(contract.category_id)
.bind(&contract.amount_total_display)
.bind(&contract.amount_by_ds)
.bind(&contract.comment)