fix
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use che_tauri::{Filter, FilterSet};
|
||||
|
||||
use super::models::{Contract, ContractApplicationFile, Counterparty};
|
||||
use super::models::{Contract, ContractApplicationFile, ContractCategory, Counterparty};
|
||||
|
||||
static CONTRACTAPP_FILTERS: &[Filter] = &[
|
||||
Filter::exact("id"),
|
||||
@@ -16,6 +16,7 @@ static CONTRACTAPP_FILTERS: &[Filter] = &[
|
||||
Filter::contains("contract_type"),
|
||||
Filter::exact("category"),
|
||||
Filter::contains("category"),
|
||||
Filter::exact("category_id"),
|
||||
Filter::contains("counterparty_name"),
|
||||
Filter::contains("name_of_product"),
|
||||
Filter::exact("counterparty_id"),
|
||||
@@ -24,6 +25,14 @@ static CONTRACTAPP_FILTERS: &[Filter] = &[
|
||||
Filter::exact("employee_id"),
|
||||
];
|
||||
|
||||
static CONTRACT_CATEGORY_FILTERS: &[Filter] = &[
|
||||
Filter::exact("id"),
|
||||
Filter::exact("name"),
|
||||
Filter::contains("name"),
|
||||
Filter::exact("name_group"),
|
||||
Filter::contains("name_group"),
|
||||
];
|
||||
|
||||
static COUNTERPARTY_FILTERS: &[Filter] = &[
|
||||
Filter::exact("id"),
|
||||
Filter::exact("name"),
|
||||
@@ -43,6 +52,10 @@ pub fn contractapp_filterset() -> FilterSet<Contract> {
|
||||
FilterSet::new(CONTRACTAPP_FILTERS)
|
||||
}
|
||||
|
||||
pub fn contract_category_filterset() -> FilterSet<ContractCategory> {
|
||||
FilterSet::new(CONTRACT_CATEGORY_FILTERS)
|
||||
}
|
||||
|
||||
pub fn counterparty_filterset() -> FilterSet<Counterparty> {
|
||||
FilterSet::new(COUNTERPARTY_FILTERS)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS contract_category (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
name_group TEXT NOT NULL,
|
||||
template TEXT,
|
||||
is_questionnair BOOLEAN NOT NULL,
|
||||
parent INTEGER,
|
||||
secure_group INTEGER
|
||||
);
|
||||
|
||||
ALTER TABLE contractapp ADD COLUMN category_id INTEGER REFERENCES contract_category(id);
|
||||
@@ -173,6 +173,88 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "contract_category",
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"ty": "integer",
|
||||
"primary_key": true,
|
||||
"nullable": false,
|
||||
"auto": true,
|
||||
"unique": false,
|
||||
"max_length": null,
|
||||
"default": null,
|
||||
"foreign_key": null
|
||||
},
|
||||
{
|
||||
"name": "name",
|
||||
"ty": "text",
|
||||
"primary_key": false,
|
||||
"nullable": false,
|
||||
"auto": false,
|
||||
"unique": false,
|
||||
"max_length": null,
|
||||
"default": null,
|
||||
"foreign_key": null
|
||||
},
|
||||
{
|
||||
"name": "name_group",
|
||||
"ty": "text",
|
||||
"primary_key": false,
|
||||
"nullable": false,
|
||||
"auto": false,
|
||||
"unique": false,
|
||||
"max_length": null,
|
||||
"default": null,
|
||||
"foreign_key": null
|
||||
},
|
||||
{
|
||||
"name": "template",
|
||||
"ty": "text",
|
||||
"primary_key": false,
|
||||
"nullable": true,
|
||||
"auto": false,
|
||||
"unique": false,
|
||||
"max_length": null,
|
||||
"default": null,
|
||||
"foreign_key": null
|
||||
},
|
||||
{
|
||||
"name": "is_questionnair",
|
||||
"ty": "boolean",
|
||||
"primary_key": false,
|
||||
"nullable": false,
|
||||
"auto": false,
|
||||
"unique": false,
|
||||
"max_length": null,
|
||||
"default": null,
|
||||
"foreign_key": null
|
||||
},
|
||||
{
|
||||
"name": "parent",
|
||||
"ty": "integer",
|
||||
"primary_key": false,
|
||||
"nullable": true,
|
||||
"auto": false,
|
||||
"unique": false,
|
||||
"max_length": null,
|
||||
"default": null,
|
||||
"foreign_key": null
|
||||
},
|
||||
{
|
||||
"name": "secure_group",
|
||||
"ty": "integer",
|
||||
"primary_key": false,
|
||||
"nullable": true,
|
||||
"auto": false,
|
||||
"unique": false,
|
||||
"max_length": null,
|
||||
"default": null,
|
||||
"foreign_key": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "contractapp",
|
||||
"fields": [
|
||||
@@ -242,6 +324,20 @@
|
||||
"default": null,
|
||||
"foreign_key": null
|
||||
},
|
||||
{
|
||||
"name": "category_id",
|
||||
"ty": "integer",
|
||||
"primary_key": false,
|
||||
"nullable": true,
|
||||
"auto": false,
|
||||
"unique": false,
|
||||
"max_length": null,
|
||||
"default": null,
|
||||
"foreign_key": {
|
||||
"table": "contract_category",
|
||||
"column": "id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "amount_total_display",
|
||||
"ty": "text",
|
||||
|
||||
@@ -21,6 +21,11 @@ impl AppModule for ContractappModule {
|
||||
serializers::contractapp_serializer(),
|
||||
filters::contractapp_filterset(),
|
||||
);
|
||||
ctx.resource::<models::ContractCategory>(
|
||||
"contract_category",
|
||||
serializers::contract_category_serializer(),
|
||||
filters::contract_category_filterset(),
|
||||
);
|
||||
ctx.resource::<models::Counterparty>(
|
||||
"counterparty",
|
||||
serializers::counterparty_serializer(),
|
||||
|
||||
@@ -15,6 +15,10 @@ pub struct Contract {
|
||||
pub absolute_url: String,
|
||||
pub contract_type: String,
|
||||
pub category: String,
|
||||
|
||||
#[field(foreign_key = ContractCategory)]
|
||||
pub category_id: Option<i64>,
|
||||
|
||||
pub amount_total_display: String,
|
||||
pub amount_by_ds: String,
|
||||
pub comment: String,
|
||||
@@ -48,6 +52,20 @@ pub struct Contract {
|
||||
pub employee_id: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Model)]
|
||||
#[model(table = "contract_category")]
|
||||
pub struct ContractCategory {
|
||||
#[field(primary_key)]
|
||||
pub id: i64,
|
||||
|
||||
pub name: String,
|
||||
pub name_group: String,
|
||||
pub template: Option<String>,
|
||||
pub is_questionnair: bool,
|
||||
pub parent: Option<i64>,
|
||||
pub secure_group: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Model)]
|
||||
#[model(table = "counterparty")]
|
||||
pub struct Counterparty {
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::apps::{
|
||||
projectapp::{models::Project, serializers::project_serializer},
|
||||
};
|
||||
|
||||
use super::models::{Contract, ContractApplicationFile, Counterparty};
|
||||
use super::models::{Contract, ContractApplicationFile, ContractCategory, Counterparty};
|
||||
|
||||
static CONTRACT_FIELDS: &[Field] = &[
|
||||
Field::new("id").read_only(),
|
||||
@@ -15,6 +15,7 @@ static CONTRACT_FIELDS: &[Field] = &[
|
||||
Field::new("absolute_url"),
|
||||
Field::new("contract_type"),
|
||||
Field::new("category"),
|
||||
Field::new("category_id").required(false).nullable(),
|
||||
Field::new("amount_total_display"),
|
||||
Field::new("amount_by_ds"),
|
||||
Field::new("comment"),
|
||||
@@ -38,12 +39,22 @@ static CONTRACT_FIELDS: &[Field] = &[
|
||||
Field::new("project_id").required(false).nullable(),
|
||||
Field::new("frc_id").required(false).nullable(),
|
||||
Field::new("employee_id").required(false).nullable(),
|
||||
Field::related("category_ref", "category_id", &CATEGORY_RELATION),
|
||||
Field::related("counterparty", "counterparty_id", &COUNTERPARTY_RELATION),
|
||||
Field::related("project", "project_id", &PROJECT_RELATION),
|
||||
Field::related("frc", "frc_id", &FRC_RELATION),
|
||||
Field::related("employee", "employee_id", &EMPLOYEE_RELATION),
|
||||
];
|
||||
|
||||
static CONTRACT_CATEGORY_FIELDS: &[Field] = &[
|
||||
Field::new("id").read_only(),
|
||||
Field::new("name"),
|
||||
Field::new("name_group"),
|
||||
Field::new("template").required(false).nullable(),
|
||||
Field::new("is_questionnair"),
|
||||
Field::new("parent").required(false).nullable(),
|
||||
Field::new("secure_group").required(false).nullable(),
|
||||
];
|
||||
static COUNTERPARTY_FIELDS: &[Field] = &[Field::new("id").read_only(), Field::new("name")];
|
||||
static CONTRACT_APPLICATION_FILE_FIELDS: &[Field] = &[
|
||||
Field::new("id").read_only(),
|
||||
@@ -65,6 +76,8 @@ static CONTRACT_APPLICATION_FILE_FIELDS: &[Field] = &[
|
||||
];
|
||||
static COUNTERPARTY_RELATION: RelatedModel<Counterparty> =
|
||||
RelatedModel::new(counterparty_serializer);
|
||||
static CATEGORY_RELATION: RelatedModel<ContractCategory> =
|
||||
RelatedModel::new(contract_category_serializer);
|
||||
static CONTRACT_RELATION: RelatedModel<Contract> = RelatedModel::new(contractapp_serializer);
|
||||
static PROJECT_RELATION: RelatedModel<Project> = RelatedModel::new(project_serializer);
|
||||
static FRC_RELATION: RelatedModel<Frc> = RelatedModel::new(frc_serializer);
|
||||
@@ -74,6 +87,10 @@ pub fn contractapp_serializer() -> ModelSerializer<Contract> {
|
||||
ModelSerializer::new(CONTRACT_FIELDS)
|
||||
}
|
||||
|
||||
pub fn contract_category_serializer() -> ModelSerializer<ContractCategory> {
|
||||
ModelSerializer::new(CONTRACT_CATEGORY_FIELDS)
|
||||
}
|
||||
|
||||
pub fn counterparty_serializer() -> ModelSerializer<Counterparty> {
|
||||
ModelSerializer::new(COUNTERPARTY_FIELDS)
|
||||
}
|
||||
|
||||
+102
-5
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user