revert: old rental (#983)

Reviewed-on: daoyoucloud/tachybase#983
This commit is contained in:
sealday 2024-05-14 18:20:08 +08:00
parent b762282578
commit 0602743922
37 changed files with 3083 additions and 2258 deletions

View File

@ -1,7 +1,7 @@
{
"name": "@hera/plugin-rental",
"displayName": "professional construction materials rental system - customized based on hera",
"version": "1.7.41",
"version": "1.7.42",
"description": "Offering a standardized leasing management system, encompassing comprehensive administration from materials to contracts to labor personnel, while providing a robust financial management mechanism, and real-time monitoring of the operational capacity of the leasing system.",
"keywords": [
"System management"

View File

@ -3,10 +3,10 @@ import _ from 'lodash';
import { CustomComponentType, CustomFunctionComponent } from '@hera/plugin-core/client';
import { useField, useFieldSchema, useForm } from '@tachybase/schema';
import { FormPath } from '@tachybase/schema';
import { useCollection, useRequest } from '@tachybase/client';
import { useRequest } from '@tachybase/client';
import { Descriptions, Spin } from 'antd';
import { formatQuantity } from '../../utils/currencyUtils';
import { useProducts } from '../hooks';
export const RecordDetails: CustomFunctionComponent = () => {
const form = useForm();
const fieldSchema = useFieldSchema();
@ -14,116 +14,103 @@ export const RecordDetails: CustomFunctionComponent = () => {
const path: any = field.path.entire;
const fieldPath = path?.replace(`.${fieldSchema.name}`, '');
const itemPath = FormPath.parse('.', fieldPath).toString();
const collection = useCollection();
const record_id =
collection.template === 'view' ? form.getValuesIn(itemPath).record_id : form.getValuesIn(itemPath).id;
const reqRecordItems = useRequest<any>({
resource: 'record_items',
action: 'list',
params: {
appends: ['new_product'],
appends: ['product', 'product.category'],
filter: {
record_id,
record_id: form.getValuesIn(itemPath).id,
},
pageSize: 999,
},
});
const { data: products } = useProducts();
const reqRecordItemFeeItems = useRequest<any>({
resource: 'record_contract',
resource: 'record_item_fee_items',
action: 'list',
params: {
appends: ['fees'],
appends: ['product', 'record_item', 'record_item.product'],
filter: {
record_id: { $eq: record_id },
record_item: {
record_id: form.getValuesIn(itemPath).id,
},
},
pageSize: 999,
},
});
const feeItems = {};
const productItem = {};
let feeItems = null;
// 根据关联产品名称来合并赔偿
if (reqRecordItemFeeItems.data && products) {
reqRecordItemFeeItems.data.data?.forEach((contract, index) => {
if (!contract.fees.length) return;
const movement = contract.movement === '-1' ? '出库合同' : '入库合同';
const contractfee = {};
contract.fees.forEach((value) => {
if (!value.new_product_id) return;
const productItem = products.find((product) => product.id === value.new_product_id);
const categoryProductItem = products.find((product) => product.id === productItem.parentId);
const item = products.find((product) => product.id === value.new_fee_product_id);
const categoryItem = products.find((product) => product.id === item.parentId);
if (!productItem && !item && !categoryItem) return;
const key = categoryProductItem?.id || value.new_product_id;
const label = categoryItem.name ? `${categoryItem.name}[${item.name}]` : item.name;
if (!Object.keys(contractfee).includes(key)) {
contractfee[key] = {};
if (reqRecordItemFeeItems.data) {
feeItems = reqRecordItemFeeItems.data.data?.reduce((prev, current) => {
if (current.record_item.product) {
const key = current.record_item.product.name;
if (!(key in prev)) {
prev[key] = {};
}
if (!Object.keys(contractfee).includes(categoryItem.id)) {
contractfee[key][categoryItem.id] = {
productId: key,
label,
if (!current.product) {
return prev;
}
if (!(current.product.label in prev[key])) {
prev[key][current.product.label] = {
id: current.product.id,
label: current.product.label,
count: 0,
};
}
contractfee[key][categoryItem.id].count += value.count;
});
feeItems[movement + (index + 1)] = contractfee;
});
prev[key][current.product.label].count += current.count;
}
return prev;
}, {});
}
const items = [];
const productItem = {};
if (reqRecordItems.data) {
reqRecordItems.data.data?.forEach((item) => {
if (!item.new_product_id) return;
const categoryItem = products.find((value) => value.id === item.new_product.parentId);
const key = categoryItem?.id || item.new_product.id;
const count = categoryItem?.convertible ? item.count * item.new_product.ratio : item.count;
const unit = categoryItem?.convertible ? categoryItem?.conversion_unit ?? '' : categoryItem?.unit ?? '';
const weight = item.count * item.new_product.weight;
if (reqRecordItems.data && feeItems) {
reqRecordItems.data.data?.forEach((item) => {
if (!item.product) return;
const key = item.product.name;
const count = item.product.category.convertible ? item.count * item.product.ratio : item.count;
const unit = item.product.category.convertible
? item.product.category.conversion_unit
: item.product.category.unit;
const weight = item.count * item.product.weight;
if (productItem[key]) {
productItem[key].count += count;
productItem[key].weight += weight;
} else {
productItem[key] = {
key,
label: categoryItem?.name || item.new_product.name,
key: item.product.category_id,
label: item.product.name,
sort: item.product.category.sort,
unit,
count,
weight: weight,
weight: formatQuantity(weight, 2) + 'KG',
};
}
});
for (const key in productItem) {
productItem[key]['children'] = formatQuantity(productItem[key].count, 2) + productItem[key].unit;
productItem[key]['span'] = 1;
items.push(productItem[key]);
productItem[key].children = formatQuantity(productItem[key].count, 2) + productItem[key].unit;
productItem[key].span = 1;
items.push({
label: '理论重量',
children: formatQuantity(productItem[key].weight, 2) + 'KG',
children: [productItem[key].weight],
});
if (Object.keys(feeItems).length) {
for (const fee in feeItems) {
const children = {};
children['movement'] = fee;
const feeItem = feeItems[fee][key];
if (!feeItem || !Object.keys(feeItem).length) {
items.push({
label: '维修赔偿',
children: [''],
});
} else {
children['label'] = Object.values(feeItem).reduce((pev, curr) => {
return pev + `${curr['label']}:${curr['count']}`;
}, '');
items.push({
label: '维修赔偿',
children: `${children['movement']} : ${children['label']}`,
});
if (key in feeItems) {
productItem[key].span = 1;
const children = [];
for (const feeKey in feeItems[key]) {
if (children.length > 0) {
children.push(<br />);
}
children.push(feeItems[key][feeKey].label + ' ' + formatQuantity(feeItems[key][feeKey].count, 2));
}
items.push({
label: '维修赔偿',
children,
});
} else {
items.push({
label: '维修赔偿',
@ -132,7 +119,7 @@ export const RecordDetails: CustomFunctionComponent = () => {
}
}
}
items.sort((a, b) => a.key - b.key);
items.sort((a, b) => a.sort - b.sort);
if (reqRecordItems.loading || reqRecordItemFeeItems.loading) {
return <Spin />;
}

View File

@ -1,170 +1,213 @@
import { observer } from '@tachybase/schema';
import { onFieldInit, onFieldValueChange } from '@tachybase/schema';
import { useField, useForm, useFormEffects } from '@tachybase/schema';
import { CustomComponentType, CustomFC, CustomFunctionComponent } from '@hera/plugin-core/client';
import { CustomComponentType, CustomFunctionComponent } from '@hera/plugin-core/client';
import { useRequest } from '@tachybase/client';
import _ from 'lodash';
import React, { useEffect, useState } from 'react';
import { ConversionLogics, Movement, countCource } from '../../utils/constants';
import { formatQuantity } from '../../utils/currencyUtils';
import { useCachedRequest, useFeeItems, useProductFeeItems, useProducts } from '../hooks';
import { useDeepCompareEffect } from 'ahooks';
export const RecordFeeConvertedAmount = observer((props) => {
const form = useForm();
const field = useField();
const contractsItem = form.getValuesIn(field.path.slice(0, 2).entire);
const date = form.getValuesIn('date');
const formFeeItem = form.getValuesIn(field.path.slice(0, -2).entire);
const contractPlanId = [contractsItem.contract?.id].filter(Boolean);
const { data: products } = useProducts();
const { data: feeItems } = useFeeItems(contractPlanId, date);
const { data: productFeeRuleItems } = useProductFeeItems(contractPlanId, date);
const reqWeightRules = useCachedRequest<any>({
resource: 'weight_rules',
export const RecordFeeConvertedAmount: CustomFunctionComponent = () => {
// 查数据,查里面的费用关联的数据
const contractPlans = useRequest<any>({
resource: 'contract_plans',
action: 'list',
params: {
appends: [
'lease_items',
'lease_items.products',
'lease_items.fee_items',
'lease_items.fee_items.conversion_logic',
'lease_items.fee_items.conversion_logic.weight_items',
'fee_items',
'fee_items.conversion_logic',
'fee_items.conversion_logic.weight_items',
],
pageSize: 99999,
},
});
const productCategory = useRequest<any>({
resource: 'product_category',
action: 'list',
params: {
pageSize: 99999,
},
});
const result = {};
if (
contractsItem.contract?.id &&
products &&
formFeeItem.new_product?.id &&
formFeeItem.new_fee_product?.id &&
formFeeItem.count &&
Object.keys(productFeeRuleItems).length
) {
const productItem = products.find((value) => value.id === formFeeItem.new_product.id);
const categoryItem = products.find((value) => value.id === productItem.parentId);
const feeProductItem = products.find((value) => value.id === formFeeItem.new_fee_product.id);
const productFeeItem = productFeeRuleItems[contractsItem.contract.id].find((contractItem) =>
productItem?.['parentScopeId'].includes(contractItem.new_products_id),
);
const feeItem = productFeeItem?.fee_items?.find((value) =>
feeProductItem?.['parentScopeId'].includes(value.new_fee_products_id),
);
const calc = { unit: categoryItem.unit, count: 0 };
if (feeItem.count_source === countCource.artificial) {
const { count, unit } = feeCalc(
feeItem,
productItem,
formFeeItem.count,
categoryItem,
form.values,
reqWeightRules?.data?.data,
);
calc.unit = unit;
calc.count = count;
} else if (
((feeItem.count_source === countCource.outProduct || feeItem.count_source === countCource.outItem) &&
contractsItem.movement === '-1') ||
((feeItem.count_source === countCource.enterProduct || feeItem.count_source === countCource.enterItem) &&
contractsItem.movement === '1') ||
feeItem.count_source === countCource.product ||
feeItem.count_source === countCource.item
) {
const item = form.values.items.find((value) => value.new_product.id === formFeeItem.new_product.id);
const { count, unit } = feeCalc(
feeItem,
productItem,
item.count,
categoryItem,
form.values,
reqWeightRules?.data?.data,
);
calc.unit = unit;
calc.count = count;
const [result, setResult] = useState('-');
const [items, setItems] = useState([]);
const [feeItems, setFeeItems] = useState([]);
const [recordWeight, setWeight] = useState(0);
const form = useForm();
const field = useField();
const recordData = {
items: items,
record_fee_items: feeItems,
};
const calcCount = () => {
const path = field.path.entire as string;
const newPath = path.replace('.converted_count.RecordFeeConvertedAmount', '');
const pathArray: any = newPath.split('.');
let target: any = recordData;
for (let i = 0; i < pathArray.length; i++) {
if (!isNaN(pathArray[i])) {
pathArray[i] = parseInt(pathArray[i]);
}
target = target[pathArray[i]];
}
result['unit'] = calc.unit;
result['count'] = calc.count;
} else if (
contractsItem.contract?.id &&
products &&
!formFeeItem.new_product?.id &&
formFeeItem.new_fee_product?.id &&
formFeeItem.count &&
Object.keys(feeItems).length
) {
const feeItem = products.find((value) => value.id === formFeeItem.new_fee_product.id);
const ruleItem = feeItems[contractsItem.contract.id]?.find((value) =>
feeItem?.['parentScopeId'].includes(value.new_fee_products_id),
);
const calc = { unit: ruleItem.unit, count: 0 };
if (
ruleItem.count_source === countCource.artificial ||
ruleItem.conversion_logic_id === ConversionLogics.ActualWeight
) {
calc.count = formFeeItem.count;
if (ruleItem.conversion_logic_id === ConversionLogics.ActualWeight) calc.unit = '吨';
} else if (
((ruleItem.count_source === countCource.outProduct || ruleItem.count_source === countCource.outItem) &&
contractsItem.movement === '-1') ||
((ruleItem.count_source === countCource.enterProduct || ruleItem.count_source === countCource.enterItem) &&
contractsItem.movement === '1') ||
ruleItem.count_source === countCource.product ||
ruleItem.count_source === countCource.item
) {
calc.count = [...form.values.items].reduce((prv, curr) => {
if (!curr.new_product?.id) return prv + 0;
const productItem = products.find((value) => value.id === curr.new_product.id);
const categoryItem = products.find((value) => value.id === productItem.parentId);
const { count, unit } = feeCalc(
ruleItem,
productItem,
curr.count,
categoryItem,
form.values,
reqWeightRules?.data?.data,
let calcValue = 0;
let feeRule;
if (form.values.contract_plan) {
const recordPlaid = contractPlans.data.data.find(
(item) =>
item.id === form.values.contract_plan.id ||
(item.contract_id === form.values.contract_id &&
item.start_date <= form.values.date &&
item.end_date >= form.values.date),
);
const feeData = target;
if (pathArray.length > 2) {
// 关联产品费用
const itemData = items[pathArray[1]];
const productData = itemData?.product;
// 租金规则
const leaseRule = recordPlaid.lease_items.find((rule) =>
rule.products.find(
(product) => product.id - 99999 === productData.category_id || product.id === productData.id,
),
);
return prv + count;
}, 0);
// 租金规则中的费用规则
feeRule = leaseRule.fee_items.find((fee) => fee.fee_product_id === feeData.product?.id);
if (!feeRule || !feeRule.conversion_logic_id || !feeRule.count_source) return;
if (feeRule.count_source === countCource.artificial) {
// 手工录入
calcValue = feeData.count || 0;
} else if (
(feeRule.count_source === countCource.outProduct && form.values.movement === Movement.out) ||
(feeRule.count_source === countCource.enterProduct && form.values.movement === Movement.in) ||
feeRule.count_source === countCource.product
) {
// 出库量, 入库量, 出入库量
calcValue = itemData.count || 0;
}
const category = productCategory.data.data.find((item) => item.id === productData.category_id);
if (
feeRule.conversion_logic_id === ConversionLogics.Keep ||
feeRule.conversion_logic_id === ConversionLogics.ActualWeight
) {
// 不必处理
} else if (feeRule.conversion_logic_id === ConversionLogics.Product) {
calcValue = category.convertible ? calcValue * productData.ratio : calcValue;
} else if (feeRule.conversion_logic_id === ConversionLogics.ProductWeight) {
calcValue = calcValue * productData.weight;
} else {
const weightItem = feeRule.conversion_logic?.weight_items.find(
(item) => item.product_id - 99999 === category.id || item.product_id === productData.id,
);
if (!weightItem) return;
if (weightItem.conversion_logic_id === ConversionLogics.Keep) {
calcValue = calcValue * weightItem.weight;
} else if (weightItem.conversion_logic_id === ConversionLogics.Product) {
calcValue = category.convertible
? calcValue * productData.ratio * weightItem.weight
: calcValue * weightItem.weight;
}
}
if (!productData) return;
// 定位组件所在的费用数据位置
} else {
feeRule = recordPlaid.fee_items.find((fee) => fee.fee_product_id === feeData.product?.id);
if (!feeRule) return;
let count;
const category = productCategory.data.data.find((item) => item.id === feeData.product?.category_id);
// 人工录入
if (feeRule.count_source === countCource.artificial) {
count = feeData.count;
if (feeRule.conversion_logic_id === ConversionLogics.Keep) {
calcValue = count;
} else if (feeRule.conversion_logic_id === ConversionLogics.Product) {
calcValue = category.convertible ? count * feeData.product?.ratio : count;
} else if (feeRule.conversion_logic_id === ConversionLogics.ProductWeight) {
calcValue = count * (feeData.product?.weight || 1);
} else if (feeRule.conversion_logic_id === ConversionLogics.ActualWeight) {
calcValue = form.values?.weight;
} else {
const weightItem = feeRule.conversion_logic?.weight_items.find((item) => {
item.product_id - 99999 === category.id || item.product_id === feeData.product.id;
});
if (!weightItem) return;
if (weightItem.conversion_logic_id === ConversionLogics.Keep) {
calcValue = count * weightItem.weight;
} else if (weightItem.conversion_logic_id === ConversionLogics.Product) {
calcValue = category.convertible
? count * feeData.product.ratio * weightItem.weight
: count * weightItem.weight;
}
}
} else {
// 出库/入库/出入库量
// 出库单,但是规则不是出库产品量
if (form.values.movement === Movement.out && feeRule.count_source === countCource.enterProduct) return;
// 入库单,但是规则不是入库产品量
if (form.values.movement === Movement.in && feeRule.count_source === countCource.outProduct) return;
count = feeData.count;
if (feeRule.conversion_logic_id === ConversionLogics.Keep) {
calcValue = count;
} else if (feeRule.conversion_logic_id === ConversionLogics.Product) {
calcValue = category.convertible ? count * feeData.product.ratio : count;
} else if (feeRule.conversion_logic_id === ConversionLogics.ProductWeight) {
calcValue = count * (feeData.product.weight || 1);
} else if (feeRule.conversion_logic_id === ConversionLogics.ActualWeight) {
calcValue = form.values.weight;
} else {
let val = 0;
items.forEach((item) => {
const weightItem = feeRule.conversion_logic?.weight_items.find(
(fee) => item.product?.id === fee.product_id - 99999 || item.product?.id === fee.product_id,
);
if (!weightItem) return;
if (weightItem.conversion_logic_id === ConversionLogics.Keep) {
val += item.count * weightItem.weight;
} else if (weightItem.conversion_logic_id === ConversionLogics.Product) {
val += category.convertible
? item.count * feeData.product?.ratio * weightItem.weight
: item.count * weightItem.weight;
}
});
calcValue = val / 1000;
}
}
}
}
result['unit'] = calc.unit;
result['count'] = calc.count;
}
useDeepCompareEffect(() => {
form.setValuesIn(field.path.entire, result);
}, [formFeeItem, result]);
return <span>{`${result['count'] || 0}${result['unit'] || ''}`}</span>;
}) as CustomFC;
setResult(formatQuantity(calcValue, 2) + (feeRule ? feeRule.unit : ''));
};
useFormEffects(() => {
onFieldInit('items.*', () => {
setItems(_.cloneDeep(form.values.items));
});
onFieldValueChange('items.*', () => {
setItems(_.cloneDeep(form.values.items));
});
onFieldInit('record_fee_items.*', () => {
setFeeItems(_.cloneDeep(form.values.record_fee_items));
});
onFieldValueChange('record_fee_items.*', () => {
setFeeItems(_.cloneDeep(form.values.record_fee_items));
});
onFieldInit('weight', () => {
setWeight(_.cloneDeep(form.values.weight));
});
onFieldValueChange('weight', () => {
setWeight(_.cloneDeep(form.values.weight));
});
});
useEffect(() => {
if (!contractPlans.loading && !productCategory.loading) {
calcCount();
}
}, [items, contractPlans, productCategory, feeItems, recordWeight]);
return <span>{result}</span>;
};
RecordFeeConvertedAmount.displayName = 'RecordFeeConvertedAmount';
RecordFeeConvertedAmount.__componentType = CustomComponentType.CUSTOM_FIELD;
RecordFeeConvertedAmount.__componentLabel = '费用 - 换算数量';
const feeCalc = (rule, item, itemCount, category, form, weightRule) => {
const calc = { count: 0, unit: '' };
if (rule.conversion_logic_id === ConversionLogics.Keep) {
calc.count = itemCount;
calc.unit = category.unit;
} else if (rule.conversion_logic_id === ConversionLogics.Product) {
calc.count = category.convertible ? item.ratio * itemCount : itemCount;
calc.unit = category.convertible ? category.conversion_unit : category.unit;
} else if (rule.conversion_logic_id === ConversionLogics.ActualWeight) {
const groupWeight =
form.group_weight_items?.find((value) => value.new_products?.id === category.id) || form.weight || 0;
calc.count = groupWeight;
calc.unit = '吨';
} else if (rule.conversion_logic_id === ConversionLogics.ProductWeight) {
calc.count = (itemCount * item.weight) / 1000;
calc.unit = '吨';
} else {
const weightRuleItem = weightRule?.find(
(weightItem) => weightItem.logic_id === rule.conversion_logic_id && weightItem.new_product_id === item.id,
);
if (weightRuleItem) {
if (weightRuleItem.conversion_logic_id === ConversionLogics.Keep) {
calc.count = (itemCount * weightRuleItem.weight) / 1000;
calc.unit = '吨';
} else if (weightRuleItem.conversion_logic_id === ConversionLogics.Product) {
calc.count = category.convertible
? (itemCount * item.ratio * weightRuleItem.weight) / 1000
: (itemCount * weightRuleItem.weight) / 1000;
calc.unit = '吨';
}
}
}
return calc;
};

View File

@ -1,64 +1,69 @@
import _ from 'lodash';
import { Spin } from 'antd';
import React, { useEffect } from 'react';
import React from 'react';
import { observer, useField, useFieldSchema, useForm } from '@tachybase/schema';
import { CustomComponentType, CustomFC } from '@hera/plugin-core/client';
import { useCachedRequest, useFeeItems, useProductFeeItems, useProducts } from '../hooks';
import { useFeeItems } from '../hooks';
import { useDeepCompareEffect } from 'ahooks';
export const RecordFeeScope = observer(() => {
const form = useForm();
const fieldSchema = useFieldSchema();
const field = useField();
const contractsItem = form.getValuesIn(field.path.slice(0, 2).entire);
const date = form.getValuesIn('date');
const productsItem = form.getValuesIn(field.path.slice(0, -2).entire);
const contractPlanId = [contractsItem.contract?.id].filter(Boolean);
const { data: products } = useProducts();
const { data: feeItems } = useFeeItems(contractPlanId, date);
const { data: productFeeItems } = useProductFeeItems(contractPlanId, date);
const result = [];
const feeScope = { scopeItem: {} };
if (contractsItem.contract?.id && products && productsItem.new_product?.id && Object.values(productFeeItems).length) {
const productItem = products.find((value) => value.id === productsItem.new_product.id);
const productFeeItem = productFeeItems[contractsItem.contract?.id]?.find((contractItem) =>
productItem?.['parentScopeId'].includes(contractItem.new_products_id),
const path: any = field.path.entire;
const fieldPath = path?.replace(`.${fieldSchema.name}`, '');
const item = form.getValuesIn(field.path.slice(0, -2).entire);
let _loading = false;
let contractLoading = false;
const data = {
data: [],
};
if (form.values.record_category === '1' || form.values.record_category === '0') {
let _in = [],
_out = [];
const { data: inData, loading: inLoading } = useFeeItems(
item.product?.category_id,
form.values.in_contract_plan?.id,
);
productFeeItem?.fee_items.forEach((feeItem) => {
const item = products.find((value) => value.id === feeItem.new_fee_products_id);
if (!item) return;
feeScope.scopeItem = isExist(feeScope.scopeItem, item);
});
result.push(...Object.values(feeScope.scopeItem));
} else if (contractsItem.contract?.id && products && Object.values(feeItems).length) {
feeItems[contractsItem.contract.id]?.forEach((feeItem) => {
const item = products.find((value) => value.id === feeItem.new_fee_products_id);
if (!item) return;
feeScope.scopeItem = isExist(feeScope.scopeItem, item);
});
result.push(...Object.values(feeScope.scopeItem));
_in = inData?.data || [];
contractLoading = contractLoading || inLoading;
if (form.values.record_category === '1') {
const { data: outData, loading: outLoading } = useFeeItems(
item.product?.category_id,
form.values.out_contract_plan?.id,
);
_out = outData?.data || [];
contractLoading = contractLoading || outLoading;
}
data.data = [..._in, ..._out];
} else {
const { data: origin, loading } = useFeeItems(item.product?.category_id, form.values.contract_plan?.id);
data.data = origin?.data;
_loading = _loading || loading;
}
let result = [];
if (data.data?.length > 0) {
const items = data.data as {
products: { category_id: Number }[];
fee_items: { fee_product_id: Number }[];
}[];
result = items.reduce((acc, current) => {
acc.push(...current.fee_items.map((item) => ({ id: item.fee_product_id })));
return acc;
}, []);
}
useDeepCompareEffect(() => {
form.setValuesIn(field.path.slice(0, -1).entire, result);
}, [result, form]);
return !contractsItem ? <Spin /> : <></>;
form.setValuesIn(fieldPath, result);
}, [result, form, fieldPath]);
return ((form.values.record_category === '1' || form.values.record_category === '0') && contractLoading) ||
_loading ? (
<Spin />
) : (
<></>
);
}) as CustomFC;
RecordFeeScope.displayName = 'RecordFeeScope';
RecordFeeScope.__componentType = CustomComponentType.CUSTOM_ASSOCIATED_FIELD;
RecordFeeScope.__componentLabel = '记录单 - 费用范围';
const isExist = (feeItemScope, item) => {
if (!Object.keys(feeItemScope).length) feeItemScope[item.id] = item;
const isParent = Object.values(feeItemScope)?.find(
(value) => value['parentScopeId'].includes(item.id) && item.id !== value['id'],
);
const isChildren = Object.values(feeItemScope)?.find(
(value) => item['parentScopeId'].includes(value['id']) && item.id !== value['id'],
);
if (isParent) return feeItemScope;
if (isChildren) {
delete feeItemScope[isChildren['id']];
}
feeItemScope[item.id] = item;
return feeItemScope;
};

View File

@ -4,21 +4,30 @@ import { observer, useField, useForm } from '@tachybase/schema';
import _ from 'lodash';
import { formatQuantity } from '../../utils/currencyUtils';
import { Spin } from 'antd';
import { useCachedRequest, useProducts } from '../hooks';
import { useCachedRequest } from '../hooks';
export const RecordItemCount = observer((props) => {
const form = useForm();
const field = useField();
const { data } = useProducts();
if (!data) {
const param = {
resource: 'product_category',
action: 'list',
params: {
pageSize: 99999,
},
};
const { loading, data } = useCachedRequest<any>(param);
if (!data && loading) {
return <Spin />;
}
const item = form.getValuesIn(field.path.slice(0, -2).entire);
if (item?.new_product && item?.count) {
const category = data.find((category) => category.id === item?.new_product.parentId);
if (item?.product && item?.count) {
const category = data.data?.find((category) => category.id === item?.product.category_id);
if (!category) return;
const value = category.convertible ? (item.new_product.ratio || 0) * item.count : item.count;
const value = category.convertible ? (item.product.ratio || 0) * item.count : item.count;
const unit = category.convertible ? category.conversion_unit : category.unit || '';
return <span>{formatQuantity(value, 2) + unit}</span>;
}
return <span> - </span>;

View File

@ -2,24 +2,27 @@ import React from 'react';
import { CustomComponentType, CustomFC } from '@hera/plugin-core/client';
import { observer, useField, useForm } from '@tachybase/schema';
import _ from 'lodash';
import { ConversionLogics } from '../../utils/constants';
import { ConversionLogics, RecordCategory } from '../../utils/constants';
import { formatQuantity } from '../../utils/currencyUtils';
import { useCachedRequest, useLeaseItems, useProducts } from '../hooks';
import { useCachedRequest, useLeaseItems, useProductFeeItems } from '../hooks';
// 计价数量
export const RecordItemValuationQuantity = observer((props) => {
const form = useForm();
const field = useField();
const contractPlanId = form
.getValuesIn('new_contracts')
?.map((value) => {
return value.contract?.id;
})
.filter(Boolean);
const contractPlan = form.getValuesIn('new_contracts');
const date = form.getValuesIn('date');
const contractPlanId = form.getValuesIn('contract_plan')?.id;
const inContractPlanId = form.getValuesIn('in_contract_plan')?.id;
const outContractPlanId = form.getValuesIn('out_contract_plan')?.id;
const priceItems = form.getValuesIn('price_items');
const result = [];
const { data: reqProduct } = useProducts();
const reqProduct = useCachedRequest<any>({
resource: 'product',
action: 'list',
params: {
appends: ['category'],
pageSize: 99999,
},
});
const reqWeightRules = useCachedRequest<any>({
resource: 'weight_rules',
action: 'list',
@ -27,25 +30,116 @@ export const RecordItemValuationQuantity = observer((props) => {
pageSize: 99999,
},
});
const { data: leaseItems } = useLeaseItems(contractPlanId, date);
const { data: leaseItems } = useLeaseItems(contractPlanId);
const { data: inLeaseItems } = useLeaseItems(inContractPlanId);
const { data: outLeaseItems } = useLeaseItems(outContractPlanId);
const { data: ProductFeeItems } = useProductFeeItems(contractPlanId);
const { data: inProductFeeItems } = useProductFeeItems(inContractPlanId);
const { data: outProductFeeItems } = useProductFeeItems(outContractPlanId);
const item = form.getValuesIn(field.path.slice(0, -2).entire);
if (item?.new_product && item?.count && Object.keys(leaseItems).length) {
if (item?.product && item?.count) {
// 关联产品
if (!reqProduct) {
if (!reqProduct.data) {
return;
}
const productCategory = reqProduct.find((value) => value.id === item.new_product.parentId);
contractPlan.forEach((contractPlanItem, index) => {
if (!contractPlanItem.contract) return;
const rule = leaseItems[contractPlanItem.contract.id]?.find((product) => {
return selParentId(reqProduct, item.new_product, product.new_products_id);
});
const count = subtotal(rule, item, productCategory, reqWeightRules);
const movement = contractPlanItem.movement === '-1' ? '出库' : '入库';
count && result.push({ label: movement + '合同' + (index + 1), value: count });
});
const productCategory = reqProduct.data.data?.find(
(product) => product.category_id === item.product?.category_id,
)?.category;
// 合同
if (form.values.category === RecordCategory.lease && leaseItems) {
const rule = leaseItems.data.find((leaseItem) =>
leaseItem.products.find(
(product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id,
),
);
const feeRule = ProductFeeItems?.data.find((feeItem) => feeItem.fee_product_id === item.product?.id);
let count;
if (!rule && feeRule) {
count = subtotal(feeRule, item, productCategory, reqWeightRules, 'fee');
} else {
count = subtotal(rule, item, productCategory, reqWeightRules);
}
count && result.push({ label: '合同', value: count });
}
// 采购
if (form.values.category === RecordCategory.purchase && priceItems) {
const rule = priceItems?.find(
(rule) => rule.product?.category_id === item.product?.category_id || rule.product?.id === item.product?.id,
);
if (rule) {
rule.conversion_logic_id = rule.conversion_logic?.id;
const count = subtotal(rule, item, productCategory, reqWeightRules);
count && result.push({ label: '报价', value: count });
}
}
// 暂存或结存
if (form.values.category === RecordCategory.inventory || form.values.category === RecordCategory.staging) {
result.push([{ label: '', value: '-' }]);
}
// 采购直发
if (form.values.category === RecordCategory.purchase2lease && priceItems && inLeaseItems) {
const rule = priceItems?.find(
(rule) => rule.product?.category_id === item.product?.category_id || rule.product?.id === item.product?.id,
);
if (rule) {
rule.conversion_logic_id = rule.conversion_logic.id;
const count = subtotal(rule, item, productCategory, reqWeightRules);
count && result.push({ label: '报价', value: count });
}
const leaseRule = inLeaseItems.data.find((leaseItem) =>
leaseItem.products.find(
(product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id,
),
);
const feeRule = inProductFeeItems?.data.find(
(feeItem) =>
feeItem.fee_product_id === item.product?.id || item.product?.category_id === feeItem.fee_product.category_id,
);
let count;
if (!leaseRule && feeRule) {
count = subtotal(feeRule, item, productCategory, reqWeightRules, 'fee');
} else {
count = subtotal(leaseRule, item, productCategory, reqWeightRules);
}
count && result.push({ label: '入库合同', value: count });
}
// 租赁直发
if (form.values.category === RecordCategory.lease2lease && inLeaseItems && outLeaseItems) {
const contractPlain_out = outLeaseItems.data.find((leaseItem) =>
leaseItem.products.find(
(product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id,
),
);
const contractPlain_out_fee = outProductFeeItems?.data.find(
(feeItem) =>
feeItem.fee_product_id === item.product?.id || item.product?.category_id === feeItem.fee_product.category_id,
);
let count_out;
if (!contractPlain_out && contractPlain_out_fee) {
count_out = subtotal(contractPlain_out_fee, item, productCategory, reqWeightRules, 'fee');
} else {
count_out = subtotal(contractPlain_out, item, productCategory, reqWeightRules);
}
count_out && result.push({ label: '出库合同', value: count_out });
const contractPlain_in = inLeaseItems.data.find((leaseItem) =>
leaseItem.products.find(
(product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id,
),
);
const contractPlain_in_fee = outProductFeeItems?.data.find(
(feeItem) =>
feeItem.fee_product_id === item.product?.id || item.product?.category_id === feeItem.fee_product.category_id,
);
let count_in;
if (!contractPlain_in && contractPlain_in_fee) {
count_in = subtotal(contractPlain_in_fee, item, productCategory, reqWeightRules, 'fee');
} else {
count_in = subtotal(contractPlain_in, item, productCategory, reqWeightRules);
}
count_in && result.push({ label: '入库合同', value: count_in });
}
}
return (
<>
@ -62,17 +156,21 @@ RecordItemValuationQuantity.displayName = 'RecordItemValuationQuantity';
RecordItemValuationQuantity.__componentType = CustomComponentType.CUSTOM_FIELD;
RecordItemValuationQuantity.__componentLabel = '记录单 - 明细 - 计价数量';
const subtotal = (rule: any, itemData: any, productCategory: any, reqWeightRules: any) => {
const subtotal = (rule: any, itemData: any, productCategory: any, reqWeightRules: any, item?: any) => {
let count: number;
let unit: string;
if (rule?.conversion_logic_id === ConversionLogics.Keep) {
count = itemData.count;
unit = productCategory.unit || '';
unit = item ? rule.unit || '' : productCategory.unit || '';
} else if (rule?.conversion_logic_id === ConversionLogics.Product) {
count = productCategory.convertible ? itemData.count * itemData.new_product.ratio : itemData.count;
unit = productCategory.convertible ? productCategory.conversion_unit || '' : productCategory.unit || '';
count = productCategory.convertible ? itemData.count * itemData.product.ratio : itemData.count;
unit = item
? rule.unit || ''
: productCategory.convertible
? productCategory.conversion_unit || ''
: productCategory.unit || '';
} else if (rule?.conversion_logic_id === ConversionLogics.ProductWeight) {
count = (itemData.count * itemData.weight) / 1000;
count = item ? itemData.count : (itemData.count * itemData.product.weight) / 1000;
unit = '吨';
} else if (rule?.conversion_logic_id === ConversionLogics.ActualWeight) {
count = 0;
@ -80,29 +178,23 @@ const subtotal = (rule: any, itemData: any, productCategory: any, reqWeightRules
} else {
// 查询重量规则
const weightRule = reqWeightRules?.data?.data?.find(
(weightItem) =>
weightItem.logic_id === rule?.conversion_logic_id && weightItem.new_product_id === itemData.new_product.id,
(weight_item) =>
weight_item.logic_id === rule?.conversion_logic_id &&
(weight_item.product_id === itemData.product?.id ||
weight_item.product_id === itemData.product?.category_id + 99999),
);
if (weightRule?.conversion_logic_id === ConversionLogics.Keep) {
count = ((itemData.count || 0) * weightRule.weight) / 1000;
if (!weightRule) return;
if (weightRule.conversion_logic_id === ConversionLogics.Keep) {
count = item ? (itemData.count || 0) * weightRule.weight : ((itemData.count || 0) * weightRule.weight) / 1000;
unit = '吨';
} else if (weightRule?.conversion_logic_id === ConversionLogics.Product) {
const sacl = productCategory.convertible ? itemData.ratio : 1;
count = ((itemData.count || 0) * sacl * weightRule.weight) / 1000;
} else if (weightRule.conversion_logic_id === ConversionLogics.Product) {
const sacl = productCategory.convertible ? itemData.product.ratio : 1;
count = item
? (itemData.count || 0) * sacl * weightRule.weight
: ((itemData.count || 0) * sacl * weightRule.weight) / 1000;
unit = '吨';
}
}
const res = count > 0 ? formatQuantity(count, 2) + unit : '-';
return res;
};
const selParentId = (products, item, priceItemsId) => {
if (item.id === priceItemsId || item?.parentId === priceItemsId) {
return item;
} else if (item.parentId) {
const productItem = products.find((value) => value.id === item.parentId);
return selParentId(products, productItem, priceItemsId);
} else {
return false;
}
};

View File

@ -8,10 +8,10 @@ export const RecordItemWeight = observer((props) => {
const form = useForm();
const field = useField();
const item = form.getValuesIn(field.path.slice(0, -2).entire);
if (item?.new_product && item?.count) {
const value = ((item.new_product.weight || 0) * item.count) / 1000;
if (item?.product && item?.count) {
const value = ((item.product.weight || 0) * item.count) / 1000;
if (value) {
return <span>{formatQuantity(value, 3) + '吨'}</span>;
return <span>{formatQuantity(value, 2) + '吨'}</span>;
}
}
return <span> - </span>;

View File

@ -4,71 +4,160 @@ import React from 'react';
import { observer, useForm } from '@tachybase/schema';
import { RecordCategory } from '../../utils/constants';
import { CustomComponentType, CustomFC } from '@hera/plugin-core/client';
import { useCachedRequest, useLeaseItems, useProducts } from '../hooks';
import { useCachedRequest, useLeaseItems, useProductFeeItems } from '../hooks';
import { useDeepCompareEffect } from 'ahooks';
export const RecordProductScope = observer(() => {
const form = useForm();
const contractPlanId = form
.getValuesIn('new_contracts')
?.map((value) => {
return value.contract?.id;
})
.filter(Boolean);
const contractPlan = form.getValuesIn('new_contracts');
const date = form.getValuesIn('date');
const { data: products } = useProducts();
const { data: leaseItems } = useLeaseItems(contractPlanId, date);
const result = [];
if (contractPlanId?.length && products && Object.keys(leaseItems).length) {
const leaseItem = {};
contractPlan.forEach((contractPlanItem) => {
leaseItems[contractPlanItem.contract?.id]?.forEach((contractItem) => {
if (!contractItem.new_products?.id) return;
const item = products.find((value) => value.id === contractItem.new_products.id);
const isParent = Object.values(leaseItem).find(
(value) => value['parentScopeId'].includes(item.id) && item.id !== value['id'],
);
const isChildren = Object.values(leaseItem).find(
(value) => item['parentScopeId'].includes(value['id']) && item.id !== value['id'],
);
if (isParent) return;
if (isChildren) {
delete leaseItem[isChildren['id']];
leaseItem[item.id] = item;
} else {
leaseItem[item.id] = item;
}
const contractPlanId = form.getValuesIn('contract_plan')?.id;
const inContractPlanId = form.getValuesIn('in_contract_plan')?.id;
const outContractPlanId = form.getValuesIn('out_contract_plan')?.id;
let required = { price: false, contract: false, inContract: false, outContract: false };
const { data, loading } = useCachedRequest<any>({
resource: 'product',
action: 'list',
params: {
pageSize: 99999,
},
});
const { data: leaseItems, loading: leaseItemsLoading } = useLeaseItems(contractPlanId);
const { data: feeseItems, loading: feeItemsLoading } = useProductFeeItems(contractPlanId);
const { data: inLeaseItems, loading: inLeaseItemsLoading } = useLeaseItems(inContractPlanId);
const { data: inFeeseItems, loading: inFeeItemsLoading } = useProductFeeItems(inContractPlanId);
const { data: outLeaseItems, loading: outLeaseItemsLoading } = useLeaseItems(outContractPlanId);
const { data: outFeeseItems, loading: outFeeItemsLoading } = useProductFeeItems(outContractPlanId);
/**
* trans
*/
const transFee = (fee: any[]) => {
if (fee) {
const feeData = fee.map((fee) => {
const data = {
...fee,
};
data.products = [data.fee_product];
return data;
});
});
result.push(...Object.values(leaseItem));
} else if (!contractPlanId?.length && products) {
const leaseItem = {};
products.forEach((item) => {
const isParent = Object.values(leaseItem).find(
(value) => value['parentScopeId'].includes(item.id) && item.id !== value['id'],
);
const isChildren = Object.values(leaseItem).find(
(value) => item['parentScopeId'].includes(value['id']) && item.id !== value['id'],
);
if (isParent) return;
if (isChildren) {
delete leaseItem[isChildren['id']];
leaseItem[item.id] = item;
} else {
leaseItem[item.id] = item;
}
});
result.push(...Object.values(leaseItem));
return feeData || [];
} else {
return [];
}
};
/**
* /id或category_id数据
*/
const contractLeaseFee = (leaseData, feeData) => {
const data =
[...leaseData, ...transFee(feeData)].reduce((acc, current) => {
acc.push(
...current.products.map((item) => {
if (item.id < 99999) {
return { id: item.id };
} else {
return { category_id: item.id - 99999 };
}
}),
);
return acc;
}, []) ?? [];
return data;
};
const contractProducts = contractLeaseFee(leaseItems?.data || [], feeseItems?.data);
const inContractProducts = contractLeaseFee(inLeaseItems?.data || [], inFeeseItems?.data);
const inContractFee = contractLeaseFee([], inFeeseItems?.data);
const outContractProducts = contractLeaseFee(outLeaseItems?.data || [], outFeeseItems?.data);
const priceProducts =
form.values.price_items
?.filter((item) => item.product)
.map((item) => {
if (item.product.id > 99999) {
return {
category_id: item.product.category_id,
};
} else {
return {
id: item.product.id,
};
}
}) ?? [];
switch (form.values.category) {
case RecordCategory.lease:
required = { price: false, contract: true, inContract: false, outContract: false };
break;
case RecordCategory.purchase:
required = { price: true, contract: false, inContract: false, outContract: false };
break;
case RecordCategory.lease2lease:
required = { price: false, contract: false, inContract: true, outContract: true };
break;
case RecordCategory.purchase2lease: // 报价,入库合同的产品范围交集, 加入入库合同的费用数据
required = { price: true, contract: false, inContract: true, outContract: false };
break;
case RecordCategory.inventory:
case RecordCategory.staging:
required = { price: false, contract: false, inContract: false, outContract: false };
break;
default:
break;
}
let result = data?.data ?? [];
if (required.inContract && required.outContract) {
// 租赁直发单
const intersection = [
_.intersectionBy(inContractProducts, outContractProducts, 'id'),
_.intersectionBy(inContractProducts, outContractProducts, 'category_id'),
].flat();
const data = intersectionByMultiple(result, intersection, 'category_id');
result = [_.intersectionBy(result, intersection, 'id'), data].flat();
} else if (required.price && required.inContract) {
const intersection = [
_.intersectionBy(inContractProducts, priceProducts, 'id'),
_.intersectionBy(inContractProducts, priceProducts, 'category_id'),
].flat();
intersection.push(...inContractFee);
const data = intersectionByMultiple(result, intersection, 'category_id');
result = [_.intersectionBy(result, intersection, 'id'), data].flat();
} else if (required.price) {
const data = intersectionByMultiple(result, priceProducts, 'category_id');
result = [_.intersectionBy(result, priceProducts, 'id'), data].flat();
} else if (required.contract) {
const data = intersectionByMultiple(result, contractProducts, 'category_id');
result = [_.intersectionBy(result, contractProducts, 'id'), data].flat();
}
useDeepCompareEffect(() => {
form.setValues({
product_scope: result,
});
}, [result, form]);
return !Object.keys(leaseItems).length && !products ? <Spin /> : <></>;
return loading ||
leaseItemsLoading ||
inLeaseItemsLoading ||
outLeaseItemsLoading ||
feeItemsLoading ||
inFeeItemsLoading ||
outFeeItemsLoading ? (
<Spin />
) : (
<></>
);
}) as CustomFC;
RecordProductScope.displayName = 'RecordProductScope';
RecordProductScope.__componentType = CustomComponentType.CUSTOM_ASSOCIATED_FIELD;
RecordProductScope.__componentLabel = '记录单 - 产品范围';
const intersectionByMultiple = (arr1, arr2, propName) => {
const result = [];
arr1.forEach((item) => {
const index = arr2.findIndex((i) => i[propName] === item[propName]);
if (index !== -1) {
result.push(item);
}
});
return result;
};

View File

@ -1,22 +1,21 @@
import { observer, useForm } from '@tachybase/schema';
import { ConversionLogics } from '../../utils/constants';
import { ConversionLogics, RecordCategory } from '../../utils/constants';
import { Descriptions, Tabs } from 'antd';
import { CustomComponentType, CustomFC } from '@hera/plugin-core/client';
import { CustomComponentType, CustomFC, CustomFunctionComponent } from '@hera/plugin-core/client';
import React from 'react';
import _ from 'lodash';
import { formatCurrency, formatQuantity } from '../../utils/currencyUtils';
import { useCachedRequest, useLeaseItems, useProducts } from '../hooks';
import { useCachedRequest, useLeaseItems, useProductFeeItems } from '../hooks';
import { RecordItems } from '../../interfaces/records';
const cache = [];
export const RecordSummary = observer((): any => {
cache.length = 0;
const form = useForm();
const contractPlanId = form
.getValuesIn('new_contracts')
?.map((value) => {
return value.contract?.id;
})
.filter(Boolean);
const contractPlan = form.getValuesIn('new_contracts');
const date = form.getValuesIn('date');
const resultForm = form.values;
const contractPlanId = form.values.contract_plan?.id;
const inContractPlanId = form.values.in_contract_plan?.id;
const outContractPlanId = form.values.out_contract_plan?.id;
const leaseData = form.values.price_items;
const reqWeightRules = useCachedRequest<any>({
resource: 'weight_rules',
action: 'list',
@ -24,75 +23,136 @@ export const RecordSummary = observer((): any => {
pageSize: 99999,
},
});
const { data: products } = useProducts();
const reqProduct = useCachedRequest<any>({
resource: 'product',
action: 'list',
params: {
appends: ['category'],
pageSize: 99999,
},
});
//合同方案
const { data: leaseItems } = useLeaseItems(contractPlanId, date);
const { data: leaseItems } = useLeaseItems(contractPlanId);
const { data: inLeaseItems } = useLeaseItems(inContractPlanId);
const { data: outLeaseItems } = useLeaseItems(outContractPlanId);
if (!products) {
if (!reqProduct.data) {
return '';
}
const allPrice = {
name: '总金额',
total: 0,
unit: '',
};
const weight = {
name: '理论重量',
total: 0,
unit: '吨',
};
const priceWeight = {
name: '理论重量',
total: 0,
unit: '吨',
};
const contractWeight = {
name: '理论重量',
total: 0,
unit: '吨',
};
const outContractWeight = {
name: '理论重量',
total: 0,
unit: '吨',
};
// 订单分组实际重量
const recordData = form.values;
// 基础小结
const summaryProduct = {};
// 合同小结/入库合同小结
const contractSummary = {};
// 出库合同小结
const outContractSummary = {};
// 报价小结
const quoteSummary = {};
form.values.items?.forEach((item) => {
const productCategory = reqProduct.data.data?.find(
(product) => product.category_id === item.product?.category_id,
)?.category;
if (!(JSON.stringify(productCategory?.attr) === `["6"]`)) {
if (!item.product || !item.count) return;
const element = _.cloneDeep(item);
// 获取产品的分类数据信息
if (!productCategory) return;
element.product.category = productCategory;
// 基础小结
const { calc } = summary(element, null, null);
weight.total += calc.weight / 1000;
calcProductCount(summaryProduct, calc, productCategory);
// 合同小结(租赁出入库单/租赁直发单)
const resultItems = {};
if (form.values.items?.length && contractPlan?.length) {
form.values.items.forEach((item) => {
if (!item?.new_product_id && !item?.count) return;
const productItem = products.find((value) => value.id === item.new_product.id);
const productCategory = products?.find((value) => value.id === item.new_product.parentId);
resultItems['basis'] = basisItem(productCategory, item, productItem, resultItems['basis']);
contractPlan.forEach((contractPlanItem, index) => {
if (!contractPlanItem.contract) return;
const movement = contractPlanItem.movement === '-1' ? '出库' : '入库';
const record_category = contractPlanItem.contract.record_category;
const rule = leaseItems[contractPlanItem.contract.id]?.find((value) =>
productItem?.parentScopeId.includes(value.new_products_id),
);
resultItems[movement + index] = ruleItem(
record_category,
movement,
index + 1,
rule,
resultForm,
productCategory,
item,
productItem,
resultItems[movement + index],
reqWeightRules,
);
});
});
}
const trans: any[] = Object.values(resultItems)
.map((item) => {
let price = 0;
let weight = 0;
const valueItem = Object.values(item['value']).map((resultValue) => {
price += resultValue['price'] ?? 0;
weight += resultValue['weight'] ?? 0;
return {
key: resultValue['label'],
label: resultValue['label'],
children: formatQuantity(resultValue['count'], 3) + resultValue['unit'],
};
});
valueItem.unshift({
key: '理论重量',
label: '理论重量',
children: formatQuantity(weight, 3) + '吨',
});
if (item['record_category'] === '1') {
valueItem.push({
key: '总金额',
label: '总金额',
children: formatCurrency(price, 3),
});
if (form.values.category === RecordCategory.lease || form.values.category === RecordCategory.lease2lease) {
if (form.values.category === RecordCategory.lease && !leaseItems) return;
if (form.values.category === RecordCategory.lease2lease && !inLeaseItems) return;
const in_contract = form.values.category === RecordCategory.lease ? leaseItems.data : inLeaseItems.data;
const { ruleCalc } = summary(element, in_contract, reqWeightRules, recordData);
contractWeight.total += ruleCalc.weight / 1000;
calcProductCount(contractSummary, ruleCalc, productCategory);
if (form.values.category === RecordCategory.lease2lease && outLeaseItems) {
// 还需要生成出库合同小结
const out_contract = outLeaseItems.data;
const { ruleCalc } = summary(element, out_contract, reqWeightRules, recordData);
outContractWeight.total += ruleCalc.weight / 1000;
calcProductCount(outContractSummary, ruleCalc, productCategory);
}
}
// 采购出入库/采购直发小结
if (form.values.category === RecordCategory.purchase || form.values.category === RecordCategory.purchase2lease) {
const priceRules = leaseData?.map((rule) => {
return {
conversion_logic_id: rule.conversion_logic.id,
products: rule.product,
unit_price: rule.unit_price,
};
});
const { ruleCalc } = summary(element, priceRules, reqWeightRules, recordData);
priceWeight.total += ruleCalc.weight / 1000;
allPrice.total += ruleCalc.price;
calcProductCount(quoteSummary, ruleCalc, productCategory);
if (form.values.category === RecordCategory.purchase2lease && inLeaseItems) {
const in_contract = inLeaseItems.data;
const { ruleCalc } = summary(element, in_contract, null, recordData);
contractWeight.total += ruleCalc.weight / 1000;
calcProductCount(contractSummary, ruleCalc, productCategory);
}
}
}
});
summaryProduct['0'] = weight;
contractSummary['0'] = contractWeight;
outContractSummary['0'] = outContractWeight;
quoteSummary['0'] = priceWeight;
quoteSummary['999'] = allPrice;
const resultItems = [
{ label: '基础', value: transDescriptions(Object.values(summaryProduct)) },
{ label: '报价', value: transDescriptions(Object.values(quoteSummary)) },
{ label: '出库合同', value: transDescriptions(Object.values(outContractSummary)) },
{
label: form.values.category === RecordCategory.lease ? '合同' : '入库合同',
value: transDescriptions(Object.values(contractSummary)),
},
];
const trans: any[] = resultItems
.map((item) => {
if (item.value.length) {
const data = {
label: item.label,
key: item.label,
children: <Descriptions items={item.value} />,
};
return data;
}
const data = {
label: item['label'],
key: item['label'],
children: <Descriptions items={valueItem} />,
};
return data;
})
.filter(Boolean);
@ -116,144 +176,125 @@ RecordSummary.__componentType = CustomComponentType.CUSTOM_FORM_ITEM;
RecordSummary.__componentLabel = '记录单 - 小结';
/**
* @param event RecordItem record_item
*/
const summary = (rule, itemCount, category, item, ruleWeight) => {
const summary = (event: RecordItems, rules: any[], ruleWeight: any, recordData = null) => {
// 1. 没有规则直接默认产品表换算逻辑
const calc = {
name: '',
count: 0,
weight: 0,
unit: '',
};
const ruleCalc = {
name: '',
count: 0,
weight: 0,
unit: '',
price: 0,
};
calc.name = category.name;
const convertible = category.convertible;
if (rule.conversion_logic_id === ConversionLogics.Keep) {
calc.count = itemCount;
calc.unit = category.unit;
} else if (rule.conversion_logic_id === ConversionLogics.Product) {
calc.count = convertible ? itemCount * item.ratio : itemCount;
calc.unit = convertible ? category.conversion_unit : category.unit;
} else if (rule.conversion_logic_id === ConversionLogics.ProductWeight) {
calc.count = (itemCount * item.weight) / 1000;
calc.unit = '吨';
const category = event.product.category;
const convertiblen = category.convertible;
if (!rules) {
const count = convertiblen ? event.count * event.product.ratio : event.count;
const unit = convertiblen ? category.conversion_unit : category.unit;
calc.name = category.name;
calc.count = count;
calc.unit = unit;
calc.weight = event.product.weight * event.count;
} else {
const weightRule = ruleWeight?.find(
(weightItem) => weightItem.logic_id === rule.conversion_logic_id && weightItem.new_product_id === item.id,
// 2. 存在规则(实际重量情况不计算)
const plain = rules.find((item) =>
// 处理报价跟合同不同数据结构问题 [].flat()
[item.products]
.flat()
.find((product) => product?.id - 99999 === event.product.category_id || product?.id === event.product.id),
);
if (weightRule) {
if (weightRule.conversion_logic_id === ConversionLogics.Keep) {
calc.count = (itemCount * weightRule.weight) / 1000;
calc.unit = '吨';
} else if (weightRule.conversion_logic_id === ConversionLogics.Product) {
calc.count = convertible
? (itemCount * item.ratio * weightRule.weight) / 1000
: (itemCount * weightRule.weight) / 1000;
calc.unit = '吨';
if (!plain) return { ruleCalc };
ruleCalc.name = category.name;
if (plain.conversion_logic_id === ConversionLogics.Keep) {
ruleCalc.count = event.count;
ruleCalc.unit = category.unit;
} else if (plain.conversion_logic_id === ConversionLogics.Product) {
const count = convertiblen ? event.count * event.product.ratio : event.count;
const unit = convertiblen ? category.conversion_unit : category.unit;
ruleCalc.count = count;
ruleCalc.unit = unit;
} else if (plain.conversion_logic_id === ConversionLogics.ProductWeight) {
ruleCalc.count = (event.count * event.product.weight) / 1000;
ruleCalc.unit = '吨';
} else if (plain.conversion_logic_id === ConversionLogics.ActualWeight) {
const cacheData = cache.find(
(c) => c.id === plain.conversion_logic_id && c.category_id === event.product.category_id,
);
if (!cacheData) {
const weight =
recordData?.group_weight_items?.find((g) =>
g.product_categories?.find((pc) => pc.id === event.product?.category_id),
)?.weight || recordData?.weight;
cache.push({
id: plain.conversion_logic_id,
category_id: event.product?.category_id,
weight: weight,
});
ruleCalc.count = weight;
ruleCalc.unit = '吨';
}
// 合同理论重量,换算逻辑是重量表取重量表换算后的值(重量)
calc.weight = calc.count;
calc.price = calc.count * rule.unit_price;
} else {
const weightRule = ruleWeight.data?.data?.find(
(item) =>
(item.product_id === event.product.id || item.product_id - 99999 === event.product.category_id) &&
item.logic_id === plain.conversion_logic_id,
);
if (weightRule) {
if (weightRule.conversion_logic_id === ConversionLogics.Keep) {
ruleCalc.count = (event.count * weightRule.weight) / 1000;
ruleCalc.unit = '吨';
} else if (weightRule.conversion_logic_id === ConversionLogics.Product) {
ruleCalc.count = convertiblen
? (event.count * event.product.ratio * weightRule.weight) / 1000
: (event.count * weightRule.weight) / 1000;
ruleCalc.unit = '吨';
}
// 合同理论重量,换算逻辑是重量表取重量表换算后的值(重量)
ruleCalc.weight = ruleCalc.count * 1000;
ruleCalc.price = ruleCalc.count * plain.unit_price;
}
}
// 合同理论重量,非重量表规则都是取产品重量
if (plain.conversion_logic_id <= 4) {
ruleCalc.price = ruleCalc.count * plain.unit_price;
ruleCalc.weight = event.product.weight * event.count;
}
}
// 合同理论重量,非重量表规则都是取产品重量
if (rule.conversion_logic_id <= 4) {
calc.weight = (item.weight * itemCount) / 1000;
calc.price = rule.unit_price * calc.count;
}
return calc;
return { calc, ruleCalc };
};
const basisItem = (productCategory, item, productItem, basis) => {
const calc = {
count: 0,
unit: '',
label: '',
weight: 0,
};
calc.count = productCategory?.convertible ? item.count * productItem.ratio : item.count;
calc.weight = (item.count * productItem.weight) / 1000;
calc.unit = productCategory.convertible ? productCategory.conversion_unit : productCategory.unit;
calc.label = productCategory.name;
if (!basis) {
basis = {
label: '基础',
value: {},
};
}
const basisProduct = basis.value[productCategory.id];
if (basisProduct) {
basisProduct.count += calc.count;
basisProduct.weight += calc.weight;
/**
*
*/
const calcProductCount = (summary: any, calc: any, category: any) => {
if (summary[category.id]) {
summary[category.id].total += calc.count;
} else {
basis.value[productCategory.id] = { label: calc.label, count: calc.count, unit: calc.unit, weight: calc.weight };
}
return basis;
};
const ruleItem = (
record_category,
movement,
index,
rule,
resultForm,
productCategory,
item,
productItem,
contract,
reqWeightRules,
) => {
const calc = {
count: 0,
unit: '',
label: '',
weight: 0,
price: 0,
};
if (!contract) {
contract = {
label: movement + '合同' + index,
record_category,
value: {},
};
}
if (!rule) return contract;
if (rule.conversion_logic_id === ConversionLogics.ActualWeight) {
const groupWeight =
resultForm.group_weight_items?.find((value) => value.new_products?.id === productCategory.id) ||
resultForm.weight;
calc.label = productCategory.name;
calc.count = groupWeight ?? 0;
calc.unit = '吨';
calc.price = calc.count * rule.unit_price;
calc.weight = (item.count * productItem.weight) / 1000;
} else {
const {
name: ruleName,
count: ruleCount,
weight: ruleWeight,
unit: ruleUnit,
price: rulePrice,
} = summary(rule, item.count, productCategory, productItem, reqWeightRules?.data?.data);
calc.weight = ruleWeight;
calc.price = rulePrice;
calc.label = ruleName;
calc.count = ruleCount;
calc.unit = ruleUnit;
}
const product = contract.value[productCategory.id];
if (product) {
product.count += calc.count;
product.price += calc.price;
product.weight += calc.weight;
} else {
contract.value[productCategory.id] = {
label: calc.label,
count: calc.count,
summary[category.id] = {
name: calc.name,
total: calc.count,
unit: calc.unit,
price: calc.price,
weight: calc.weight,
};
}
return contract;
};
const transDescriptions = (data) => {
const values = data.map((item: any, index) => {
if (item.total) {
return {
key: item.name,
label: item.name,
children: item.name === '总金额' ? formatCurrency(item.total, 2) : formatQuantity(item.total, 3) + item.unit,
};
}
});
return values.filter(Boolean);
};

View File

@ -32,7 +32,6 @@ export const RecordTotalPrice: CustomFunctionComponent = () => {
const [products, setProducts] = useState([]);
const [groupWeight, setGroupWeight] = useState([]);
const [recordWeight, setRecordWeight] = useState(0);
return;
// 总金额计算方法
const computeTotalPrice = () => {
if (!leaseData) return;

View File

@ -1,177 +1,90 @@
import { useAPIClient, useRequest } from '@tachybase/client';
import dayjs from 'dayjs';
import { useRequest } from '@tachybase/client';
import { stringify } from 'flatted';
import { useEffect, useState } from 'react';
import { useEffect } from 'react';
export function useCachedRequest<P>(params: {}, options = {}) {
const cacheKey = stringify(params);
return useRequest<P>(params, { cacheKey, ...options });
}
export const useLeaseItems = (planId, date) => {
const [data, setData] = useState({});
const api = useAPIClient();
const day = date || new Date();
const nowDay = dayjs(day).startOf('day').add(-1, 'minute');
if (typeof planId !== 'object') return { data };
planId?.forEach((value) => {
if (!value) return;
if (!data[value]) {
api
.request({
resource: 'contract_plan_lease_items',
action: 'list',
params: {
appends: ['new_products'],
filter: {
effect_contracts: {
start_date: { $dateBefore: nowDay },
end_date: { $dateAfter: nowDay },
contract_id: {
$eq: value,
},
},
},
pageSize: 99999,
},
})
.then((res) => {
const result = { ...data };
result[value] = res.data?.data;
setData(result);
})
.catch(() => {
return;
});
}
});
return { data };
};
export const useFeeItems = (planId, date) => {
const [data, setData] = useState({});
const api = useAPIClient();
const day = date || new Date();
const nowDay = dayjs(day).startOf('day').add(-1, 'minute');
if (typeof planId !== 'object') return { data };
planId?.forEach((value) => {
if (!value) return;
if (!data[value]) {
api
.request({
resource: 'contract_plan_fee_items',
action: 'list',
params: {
appends: ['new_fee_products'],
filter: {
effect_contracts: {
start_date: { $dateBefore: nowDay },
end_date: { $dateAfter: nowDay },
contract_id: {
$eq: value,
},
},
},
pageSize: 99999,
},
})
.then((res) => {
const result = { ...data };
result[value] = res.data?.data;
setData(result);
})
.catch(() => {
return;
});
}
});
return { data };
};
export const useProductFeeItems = (planId, date) => {
const [data, setData] = useState({});
const api = useAPIClient();
const day = date || new Date();
const nowDay = dayjs(day).startOf('day').add(-1, 'minute');
if (typeof planId !== 'object') return { data };
planId?.forEach((value) => {
if (!value) return;
if (!data[value]) {
api
.request({
resource: 'contract_plan_lease_items',
action: 'list',
params: {
appends: ['fee_items', 'new_products'],
filter: {
effect_contracts: {
start_date: { $dateBefore: nowDay },
end_date: { $dateAfter: nowDay },
contract_id: {
$eq: value,
},
},
},
pageSize: 99999,
},
})
.then((res) => {
const result = { ...data };
result[value] = res.data?.data;
setData(result);
})
.catch(() => {
return;
});
}
});
return { data };
};
export const useProducts = () => {
const { data } = useCachedRequest<any>({
resource: 'products',
export const useLeaseItems = (planId) => {
const params = {
resource: 'contract_plan_lease_items',
action: 'list',
params: {
appends: ['products'],
filter: {
contract_plan_id: planId,
},
pageSize: 99999,
},
};
const { data, loading, run } = useCachedRequest<any>(params, {
manual: true,
});
if (data?.data) {
data.data.forEach((value) => {
value['parentScopeId'] = selParentId(data.data, value, []);
});
}
return { data: data?.data };
};
export const useCompany = (appends, id) => {
const { data, loading, run } = useCachedRequest<any>(
{
resource: 'contracts',
action: 'list',
params: {
filter: { id: { $eq: id } },
appends,
},
},
{ manual: true },
);
useEffect(() => {
if (id) {
if (planId) {
run();
}
}, [id]);
}, [planId]);
return { data, loading };
};
const selParentId = (products, item, scopeId) => {
scopeId.push(item.id);
if (!item.parentId) {
return scopeId;
}
const items = products.find((value) => value.id === item.parentId);
if (!items) {
return scopeId;
}
return selParentId(products, items, scopeId);
export const useProductFeeItems = (planId) => {
const feeParams = {
resource: 'contract_plan_fee_items',
action: 'list',
params: {
appends: ['fee_product'],
filter: {
contract_plan_id: planId,
},
pageSize: 99999,
},
};
const { data, loading, run } = useCachedRequest<any>(feeParams, {
manual: true,
});
useEffect(() => {
if (planId) {
run();
}
}, [planId]);
return { data, loading };
};
export const useFeeItems = (categoryId, planId) => {
const { data, loading, run } = useCachedRequest<any>(
{
resource: 'contract_plan_lease_items',
action: 'list',
params: {
appends: ['fee_items', 'products'],
filter: {
$and: [
{
contract_plan_id: planId,
},
{
products: {
category_id: categoryId ?? -1,
},
},
],
},
pageSize: 99999,
},
},
{
manual: true,
},
);
useEffect(() => {
if (planId && categoryId) {
run();
}
}, [planId, categoryId]);
return { data, loading };
};

View File

@ -2,11 +2,6 @@ import { useRecord } from '@tachybase/client';
import React, { createContext, useContext, useMemo, useState } from 'react';
import { SettlementStyleContext } from '../schema-initializer/actions/SettlementStyleSwitchActionInitializer';
export const PdfPaperSwitchingContext = createContext({
paper: null,
setPaper: null,
});
export const PdfIsDoubleContext = createContext({
isDouble: null,
setIsDouble: null,
@ -22,61 +17,27 @@ export const PdfMargingTopContext = createContext({
setMargingTop: null,
});
// 页面缩放
export const FontSizeContext = createContext({
size: null,
setSize: null,
});
// 注释
export const AnnotateContext = createContext({
annotate: null,
setAnnotate: null,
});
export const PdfIsDoubleProvider = (props) => {
const [isDouble, setIsDouble] = useState(false);
const [settingType, setSettingLoad] = useState(false);
const [margingTop, setMargingTop] = useState(0);
const [paper, setPaper] = useState('A4');
const [size, setSize] = useState('9');
const [annotate, setAnnotate] = useState(false);
return (
<AnnotateContext.Provider value={{ annotate, setAnnotate }}>
<PdfPaperSwitchingContext.Provider value={{ paper, setPaper }}>
<PdfMargingTopContext.Provider value={{ margingTop, setMargingTop }}>
<PdfIsDoubleContext.Provider value={{ isDouble, setIsDouble }}>
<PdfIsLoadContext.Provider value={{ settingType, setSettingLoad }}>
<FontSizeContext.Provider value={{ size, setSize }}>{props.children}</FontSizeContext.Provider>
</PdfIsLoadContext.Provider>
</PdfIsDoubleContext.Provider>
</PdfMargingTopContext.Provider>
</PdfPaperSwitchingContext.Provider>
</AnnotateContext.Provider>
<PdfMargingTopContext.Provider value={{ margingTop, setMargingTop }}>
<PdfIsDoubleContext.Provider value={{ isDouble, setIsDouble }}>
<PdfIsLoadContext.Provider value={{ settingType, setSettingLoad }}>{props.children}</PdfIsLoadContext.Provider>
</PdfIsDoubleContext.Provider>
</PdfMargingTopContext.Provider>
);
};
export const useRecordPdfPath = () => {
const record = useRecord();
let recordId = record.id || record.record_id;
let stockId;
if (record.__collectionName === 'contracts' && record.__parent) {
// 查看页面中间表id
recordId = record.__parent.id;
}
if (record.__collectionName === 'record_stock') {
stockId = record.id;
}
const { isDouble } = useContext(PdfIsDoubleContext);
const { settingType } = useContext(PdfIsLoadContext);
const { margingTop } = useContext(PdfMargingTopContext);
const { paper } = useContext(PdfPaperSwitchingContext);
const { size } = useContext(FontSizeContext);
const { annotate } = useContext(AnnotateContext);
const path = useMemo(
() =>
`/records:pdf?recordId=${recordId}&stockId=${stockId}&isDouble=${isDouble}&settingType=${settingType}&margingTop=${margingTop}&paper=${paper}&font=${size}&annotate=${annotate}`,
[recordId, isDouble, settingType, margingTop, paper, size, annotate, stockId],
() => `/records:pdf?recordId=${record.id}&isDouble=${isDouble}&settingType=${settingType}&margingTop=${margingTop}`,
[record.id, isDouble, settingType, margingTop],
);
return path;
};

View File

@ -49,16 +49,6 @@ import {
PrintSetupMargingTop,
} from './schema-initializer/actions/RecordPrintSetupMargingTopInitializer';
import { UnusedRecordsBlockHelper } from './schema-initializer/blocks/UnusedRecordsBlockInitializer';
import { PaperSwitching, PaperSwitchingInitializer } from './schema-initializer/actions/paperSwitching';
import {
RecordPrintAnnotateActionInitializer,
Annotate,
} from './schema-initializer/actions/RecordPrintAnnotateActionInitializer';
import { PrintFontSize, PrintFontSizeInitializer } from './schema-initializer/actions/RecordPrintFontSizeInitializer';
import { MovementFieldInterface } from './interfaces/movement';
import { Movement } from './schema-components/Movement';
import { MovementStatus } from './custom-components/MovementStatus';
export class PluginRentalClient extends Plugin {
locale: Locale;
async afterAdd() {}
@ -94,44 +84,26 @@ export class PluginRentalClient extends Plugin {
type: 'item',
title: '{{t("Column switch")}}',
component: 'ColumnSwitchActionInitializer',
// useVisible() {
// const collection = useCollection();
// const name = collection['options']['name'];
// return name === 'records';
// },
useVisible() {
const collection = useCollection();
const name = collection['options']['name'];
return name === 'records';
},
});
this.app.schemaInitializerManager.addItem('PDFViewActionInitializer', 'enbaleActions.paperSwitching', {
type: 'item',
title: '{{t("paper switching")}}',
component: 'PaperSwitchingInitializer',
});
this.app.schemaInitializerManager.addItem('PDFViewActionInitializer', 'enbaleActions.paperSwitching', {
type: 'item',
title: '{{t("font size")}}',
component: 'PrintFontSizeInitializer',
});
this.app.schemaInitializerManager.addItem('PDFViewActionInitializer', 'enbaleActions.recordPrintSetup', {
type: 'item',
title: '{{t("Record print setup")}}',
component: 'RecordPrintSetupActionInitializer',
});
this.app.schemaInitializerManager.addItem('PDFViewActionInitializer', 'enbaleActions.recordPrintAnnotate', {
type: 'item',
title: '{{t("Record print annotate")}}',
component: 'RecordPrintAnnotateActionInitializer',
});
this.app.schemaInitializerManager.addItem('PDFViewActionInitializer', 'enbaleActions.recordPrintMargingTop', {
type: 'item',
title: '{{t("Record print margingtop")}}',
component: 'RecordPrintSetupMargingTopInitializer',
// useVisible() {
// const collection = useCollection();
// const name = collection['options']['name'];
// return name === 'records' || name === 'waybills';
// },
useVisible() {
const collection = useCollection();
const name = collection['options']['name'];
return name === 'records' || name === 'waybills';
},
});
this.app.schemaInitializerManager.addItem('PDFViewActionInitializer', 'enbaleActions.settlementExcelExport', {
type: 'item',
@ -185,16 +157,9 @@ export class PluginRentalClient extends Plugin {
// You can get and modify the app instance here
async load() {
this.locale = new Locale(this.app);
this.app.dataSourceManager.addFieldInterfaceGroups({
bussiness: {
label: 'Bussiness',
},
});
this.app.dataSourceManager.addFieldInterfaces([MovementFieldInterface]);
this.app.addComponents({
RecordFeeConvertedAmount,
ReadFeeConvertedAmount,
Movement,
RecordFeeScope,
RecordItemValuationQuantity,
RecordItemWeight,
@ -212,12 +177,6 @@ export class PluginRentalClient extends Plugin {
PDFViewerCountablePrintActionInitializer,
ColumnSwitchActionInitializer,
ColumnSwitchAction,
PaperSwitching,
PrintFontSize,
Annotate,
RecordPrintAnnotateActionInitializer,
PaperSwitchingInitializer,
PrintFontSizeInitializer,
SettlementExcelExportActionInitializer,
SettlementStyleProvider,
SettlementStyleSwitchActionInitializer,
@ -226,7 +185,6 @@ export class PluginRentalClient extends Plugin {
RecordPrintSetupMargingTopInitializer,
PrintSetup,
PrintSetupMargingTop,
MovementStatus,
});
this.app.addScopes({
useAddToChecklistActionProps,

View File

@ -2,7 +2,7 @@ import React, { useContext, useEffect, useState } from 'react';
import { ActionInitializer } from '@tachybase/client';
import { Radio, RadioChangeEvent } from 'antd';
import { useRequest } from '@tachybase/client';
import { PdfIsLoadContext, ScaleContext } from '../../hooks/usePdfPath';
import { PdfIsLoadContext } from '../../hooks/usePdfPath';
export const PrintSetup = (props) => {
const [value, setValue] = useState('');
const settingsData = useRequest<any>({

View File

@ -56,8 +56,8 @@ export const excelDataHandle = (excelData) => {
*/
const nameRows = [
{
companyName: { name: '承租单位:', value: `${contracts.party_b?.name ?? ''}` },
companyName1: { name: '合同编号:', value: `${contracts.number ?? ''}` },
companyName: { name: '承租单位:', value: `${contracts.project?.company?.name ?? ''}` },
companyName1: { name: '合同编号:', value: `${contracts.project?.code ?? ''}` },
rowId: '3',
},
{
@ -78,7 +78,7 @@ export const excelDataHandle = (excelData) => {
name: '项目联系人:',
value: `${contracts?.project.contacts.map((contact) => contact.name + ' ' + contact.phone).join(' ')}`,
},
endData: { name: '制表人:', value: `${contracts.operator.nickname}` },
endData: { name: '经办人:', value: `${contracts.operator.nickname}` },
rowId: '6',
},
];
@ -148,7 +148,7 @@ export const excelDataHandle = (excelData) => {
parseFloat(calc.n_compensate.toFixed(2)),
parseFloat(calc.h_compensate.toFixed(2)),
parseFloat(calc.loadfreight.toFixed(2)),
parseFloat(calc.other ? calc.other.toFixed(2) : 0),
parseFloat(calc.other.toFixed(2)),
formatPercent(calc.tax, 2),
parseFloat(calc.current_expenses.toFixed(2)),
parseFloat(calc.accumulate.toFixed(2)),
@ -170,34 +170,30 @@ export const excelDataHandle = (excelData) => {
columns: [
{
name: '物料名称',
key: 'name1',
},
{
name: ' ',
key: '',
},
{
name: '费用类别',
key: 'name2',
},
{
name: '单位',
key: 'name3',
},
{
name: '订单数量',
key: 'name4',
},
{
name: '出入库数量',
key: 'name5',
},
{
name: '租赁单价',
key: 'name',
},
{
name: ' ',
key: ' ',
},
{
name: '费用类别',
key: 'name',
},
{
name: '单位',
key: 'name',
},
{
name: '订单数量',
key: 'name',
},
{
name: '出入库数量',
key: 'name',
},
{
name: '租赁单价',
key: 'name',
},
{
@ -210,7 +206,7 @@ export const excelDataHandle = (excelData) => {
},
{
name: ' ',
key: '',
key: ' ',
},
],
rows: [],
@ -225,7 +221,6 @@ export const excelDataHandle = (excelData) => {
parseFloat(value.item_count?.toFixed(2) || 0),
parseFloat(value.count.toFixed(2)),
parseFloat(value.unit_price.toFixed(5)),
'',
value.days,
parseFloat(value.amount.toFixed(2)),
'',
@ -258,10 +253,6 @@ export const excelDataHandle = (excelData) => {
name: '物质名称',
key: 'name',
},
{
name: ' ',
key: '',
},
{
name: '费用类别',
key: 'name',
@ -300,7 +291,6 @@ export const excelDataHandle = (excelData) => {
converDate(value.date, 'YYYY-MM-DD'),
value.movement === '-1' ? '出库' : value.movement === '1' ? '入库' : '出入库',
value.name,
'',
category,
value.unit_name,
parseFloat(value.item_count?.toFixed(2) || 0),
@ -365,10 +355,6 @@ export const excelDataHandle = (excelData) => {
name: ' ',
key: ' ',
},
{
name: ' ',
key: ' ',
},
],
rows: [],
};
@ -384,7 +370,6 @@ export const excelDataHandle = (excelData) => {
item.notes,
'',
'',
'',
]);
});
@ -428,7 +413,7 @@ export const excelDataHandle = (excelData) => {
},
{
name: ' ',
key: '',
key: ' ',
},
{
name: '单位',
@ -442,10 +427,6 @@ export const excelDataHandle = (excelData) => {
name: '结存数量',
key: 'name5',
},
{
name: ' ',
key: '',
},
],
rows: [],
};
@ -464,7 +445,6 @@ export const excelDataHandle = (excelData) => {
calc.summary[countNum]?.unit_name ?? '',
calc.summary[countNum] ? parseFloat(calc.summary[countNum]?.item_count?.toFixed(2) || 0) : '',
calc.summary[countNum] ? parseFloat(calc.summary[countNum]?.count.toFixed(2)) : '',
'',
]);
}
});
@ -478,25 +458,17 @@ export const excelDataHandle = (excelData) => {
const footRows = [
{
companyName: { name: `承租单位:${contracts.party_b?.name ?? ''}` },
companyName1: { name: `出租单位:${contracts.first_party?.name ?? PromptText.noContractedCompany}` },
companyName: { name: '制表人:' },
companyName1: { name: '审核人:' },
companyName2: { name: '验收人:' },
rowId: notesRow + 2,
},
{
companyName: { name: '承租单位项目经理:' },
companyName1: { name: '出租单位审核人:' },
companyName1: { name: '材料负责人:' },
companyName2: { name: '出租单位代表人:' },
rowId: notesRow + 4,
},
{
companyName: { name: '承租单位材料负责人:' },
companyName1: { name: '出租单位对账人:' },
rowId: notesRow + 6,
},
{
companyName: { name: '签署日期:' },
companyName1: { name: '签署日期:' },
rowId: notesRow + 8,
},
];
return {
table1,
@ -520,7 +492,7 @@ export const excelDataHandle = (excelData) => {
const excelAddTable = (tablerow, tableHeaderText, ws, table) => {
ws.getCell(`A${tablerow - 1}`).value = tableHeaderText;
ws.mergeCells(`A${tablerow - 1}:K${tablerow - 1}`);
ws.mergeCells(`A${tablerow - 1}:J${tablerow - 1}`);
ws.getCell(`A${tablerow - 1}`).alignment = { horizontal: 'center' };
ws.getCell(`A${tablerow - 1}`).border = {
top: { style: 'thin' },
@ -574,7 +546,7 @@ export const ExportToExcel = async (data) => {
value.height = 20;
});
//设置表格边框
const cols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K'];
const cols = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
const CellBorder = (row) => {
cols.forEach((value) => {
if (value === 'A' && row === `${notesRow}`) {
@ -600,7 +572,9 @@ export const ExportToExcel = async (data) => {
});
};
//设置表格表头
ws.getCell('A1').value = `${contracts.first_party?.name ?? `${PromptText.noContractedCompany}`} 对账单`;
ws.getCell('A1').value = `${
contracts.project?.associated_company?.name ?? `${PromptText.noContractedCompany}`
} `;
ws.mergeCells('A1:J1');
ws.getCell('A1').alignment = { vertical: 'middle', horizontal: 'center' };
ws.getCell('A1').font = {
@ -610,19 +584,19 @@ export const ExportToExcel = async (data) => {
row.height = 30;
//设置第一行内容
ws.getCell('A2').value = '客户各项费用明细';
ws.mergeCells('A2:K2');
ws.mergeCells('A2:J2');
ws.getCell('A2').alignment = { horizontal: 'center' };
//设置表格上层合同信息
nameRows.forEach((value) => {
ws.getCell(`A${value?.rowId}`).value = value[Object.keys(value)[0]].name + value[Object.keys(value)[0]].value;
ws.mergeCells(`A${value?.rowId}:F${value?.rowId}`);
ws.getCell(`G${value?.rowId}`).value = value[Object.keys(value)[1]].name + value[Object.keys(value)[1]].value;
ws.mergeCells(`G${value?.rowId}:K${value?.rowId}`);
ws.mergeCells(`G${value?.rowId}:J${value?.rowId}`);
});
//本期汇总
ws.getCell(`A${table1Row - 1}`).value = '本期汇总';
ws.mergeCells(`A${table1Row - 1}:K${table1Row - 1}`);
ws.mergeCells(`A${table1Row - 1}:J${table1Row - 1}`);
ws.getCell(`A${table1Row - 1}`).alignment = { horizontal: 'center' };
ws.getCell(`A${table1Row - 1}`).border = {
bottom: { style: 'thin' },
@ -642,8 +616,7 @@ export const ExportToExcel = async (data) => {
const rows24 = ws.getRows(table2Row, calc.history ? calc.history?.length + 1 : 1);
rows24.forEach((value) => {
ws.mergeCells(`A${value['_number']}:B${value['_number']}`);
ws.mergeCells(`G${value['_number']}:H${value['_number']}`);
ws.mergeCells(`J${value['_number']}:K${value['_number']}`);
ws.mergeCells(`I${value['_number']}:J${value['_number']}`);
if (value['_number'] !== table2Row) {
const row25 = ws.getRow(value['_number']);
row25.alignment = { horizontal: 'right' };
@ -658,7 +631,6 @@ export const ExportToExcel = async (data) => {
excelAddTable(table3Row, '本期明细', ws, table3);
const rows37 = ws.getRows(table3Row, calc.list ? calc.list?.length + 1 : 1);
rows37.forEach((value) => {
ws.mergeCells(`C${value['_number']}:D${value['_number']}`);
if (value['_number'] !== table3Row) {
const row38 = ws.getRow(value['_number']);
row38.alignment = { horizontal: 'right' };
@ -673,7 +645,7 @@ export const ExportToExcel = async (data) => {
ws.mergeCells(`A${value['_number']}:B${value['_number']}`);
ws.mergeCells(`C${value['_number']}:D${value['_number']}`);
ws.mergeCells(`F${value['_number']}:G${value['_number']}`);
ws.mergeCells(`H${value['_number']}:K${value['_number']}`);
ws.mergeCells(`H${value['_number']}:J${value['_number']}`);
if (value['_number'] !== table4row) {
const row38 = ws.getRow(value['_number']);
row38.alignment = { horizontal: 'right' };
@ -695,7 +667,6 @@ export const ExportToExcel = async (data) => {
rows1?.forEach((value) => {
ws.mergeCells(`A${value['_number']}:B${value['_number']}`);
ws.mergeCells(`F${value['_number']}:G${value['_number']}`);
ws.mergeCells(`J${value['_number']}:K${value['_number']}`);
if (value['_number'] !== table5Row) {
const row38 = ws.getRow(value['_number']);
row38.alignment = { horizontal: 'right' };
@ -714,8 +685,9 @@ export const ExportToExcel = async (data) => {
'承租单位收到租费单结算明细15日内未提异议即视为确认。请签字盖章后邮寄一份至' +
contracts.project?.associated_company.address;
ws.getCell(`F${notesRow}`).value = `
备注:${contracts.project?.comment ?? ''}`;
ws.mergeCells(`F${notesRow}:K${notesRow}`);
备注:${contracts.project?.comment ?? ''}
${contracts.project?.associated_company?.name ?? PromptText.noContractedCompany}`;
ws.mergeCells(`F${notesRow}:J${notesRow}`);
const url = 'http://985.so/bpw6g';
const imageUrl = await QRCode.toDataURL(url);
const imageId1 = workBook.addImage({
@ -732,9 +704,11 @@ export const ExportToExcel = async (data) => {
//设置底部签名区域
footRows.forEach((value) => {
ws.getCell(`A${value?.rowId}`).value = value[Object.keys(value)[0]].name;
ws.mergeCells(`A${value?.rowId}:E${value?.rowId}`);
ws.getCell(`F${value?.rowId}`).value = value[Object.keys(value)[1]].name;
ws.mergeCells(`F${value?.rowId}:K${value?.rowId}`);
ws.mergeCells(`A${value?.rowId}:C${value?.rowId}`);
ws.getCell(`D${value?.rowId}`).value = value[Object.keys(value)[1]].name;
ws.mergeCells(`D${value?.rowId}:G${value?.rowId}`);
ws.getCell(`H${value?.rowId}`).value = value[Object.keys(value)[2]].name;
ws.mergeCells(`H${value?.rowId}:J${value?.rowId}`);
});
const buffer = await workBook.xlsx.writeBuffer();

View File

@ -1,6 +1,4 @@
export interface RecordPdfOptions {
isDouble: number;
printSetup: String;
margingTop: number;
paper: string;
}

View File

@ -1,34 +1,22 @@
import { Record } from './record';
export interface Waybill {
record_number: string;
weight_or_amount: number;
off_date: Date;
arrival_date: Date;
unit_price: number;
side_information: string;
driver: any;
carrier: any;
consignee_contact: any;
in_stock: any;
shipper_contact: any;
out_stock: any;
payee_account: any;
payer: any;
pay_date: Date;
additional_cost: number;
products: any[];
payer_company: string;
payer_name: string;
payee_account_name: string;
payee_account_bank: string;
payee_account_number: string;
shipper_company: string;
shipper_name: string;
shipper_contact_phone: string;
shipper_address: string;
consignee_company: string;
consignee_contact_phone: string;
consignee_address: string;
consignee_name: string;
unit_price: number;
weight_or_amount: number;
arrival_date: Date;
off_date: Date;
pdfExplain: string;
record: Record;
comment: string;
shipper_contact: string;
carrier: string;
driver: string;
driver_idcard: string;
vehicles: string;
driver_phone: string;
side_information: string;
consignee_contact: string;
}

View File

@ -42,54 +42,46 @@ export class RecordPreviewController {
const query = {
where: where || {},
attributes: [
[sequelize.fn('sum', sequelize.col('record.items.count')), 'count'],
[sequelize.col('record.items.new_product_id'), 'new_product_id'],
[sequelize.col('view_records_contracts.movement'), 'movement'],
[sequelize.fn('sum', sequelize.col('items.count')), 'count'],
[sequelize.col('items.product_id'), 'product_id'],
[sequelize.col('records.movement'), 'movement'],
],
include: [
{
association: 'record',
association: 'items',
attributes: [],
include: [
{
association: 'items',
attributes: [],
},
],
},
...parsedFilterInclude,
],
group: [sequelize.col('record.items.new_product_id'), sequelize.col('view_records_contracts.movement')],
group: [sequelize.col('items.product_id'), sequelize.col('records.movement')],
order: [],
subQuery: false,
raw: true,
} as any;
const records = await ctx.db.getModel('view_records_contracts').findAll(query);
const records = await ctx.db.getModel('records').findAll(query);
if (records) {
const allProducts = await ctx.db.getRepository('products').find();
const allProducts = await ctx.db.getRepository('product').find({ appends: ['category'] });
if (!allProducts) return;
const items = {} as { [key: string]: { name: string; out: number; in: number; total: number } };
const items = {} as { [key: string]: { name: string; sort: number; out: number; in: number; total: number } };
records.forEach((item) => {
const product = allProducts.find((p) => p.id === item?.new_product_id);
const product = allProducts.find((p) => p.id === item?.product_id);
if (!product) return;
const parent = allProducts.find((p) => p.id === product.parentId);
const name = parent?.name || product.name;
if (!items[name]) {
items[name] = {
name: name,
if (!items[product.name]) {
items[product.name] = {
name: product.name,
sort: product.category.sort,
out: 0,
in: 0,
total: 0,
};
}
// 根据产品找父级产品的分类
const count = parent?.convertible || product.convertible ? item.count * product.ratio : item.count;
const count = product.category.convertible ? item.count * product.ratio : item.count;
if (item.movement === Movement.in) {
items[name].in += count;
items[name].total += count;
items[product.name].in += count;
items[product.name].total += count;
} else {
items[name].out += count;
items[name].total -= count;
items[product.name].out += count;
items[product.name].total -= count;
}
});
const result = {
@ -105,7 +97,7 @@ export class RecordPreviewController {
@Action('allweight')
async groupWeight(ctx: Context) {
const { filter } = ctx.action.params.values;
const collectionName = 'view_records_contracts';
const collectionName = 'records';
const collection = ctx.db.getCollection(collectionName);
const fields = collection.fields;
const filterParser = new FilterParser(filter, {
@ -123,22 +115,16 @@ export class RecordPreviewController {
const query = {
where: where || {},
attributes: [
[sequelize.fn('sum', sequelize.col('record.weight')), 'weight'],
[sequelize.col('view_records_contracts.movement'), 'movement'],
],
group: [sequelize.col('view_records_contracts.movement')],
include: [
{
association: 'record',
attributes: [],
},
...parsedFilterInclude,
[sequelize.fn('sum', sequelize.col('records.weight')), 'weight'],
[sequelize.col('records.movement'), 'movement'],
],
group: [sequelize.col('records.movement')],
include: [...parsedFilterInclude],
order: [],
subQuery: false,
raw: true,
} as any;
const records = await ctx.db.getModel('view_records_contracts').findAll(query);
const records = await ctx.db.getModel('records').findAll(query);
const inNum = records.find((item) => item.movement === Movement.in)?.weight ?? 0;
const outNum = records.find((item) => item.movement === Movement.out)?.weight ?? 0;
const data = {
@ -150,7 +136,7 @@ export class RecordPreviewController {
@Action('allprice')
async groupPrice(ctx: Context) {
const { filter } = ctx.action.params.values;
const collectionName = 'view_records_contracts';
const collectionName = 'records';
const collection = ctx.db.getCollection(collectionName);
const fields = collection.fields;
const filterParser = new FilterParser(filter, {
@ -168,22 +154,17 @@ export class RecordPreviewController {
const query = {
where: where || {},
attributes: [
[sequelize.fn('sum', sequelize.col('record.all_price')), 'all_price'],
[sequelize.col('view_records_contracts.movement'), 'movement'],
],
group: [sequelize.col('view_records_contracts.movement')],
include: [
{
association: 'record',
attributes: [],
},
...parsedFilterInclude,
[sequelize.fn('sum', sequelize.col('records.all_price')), 'all_price'],
[sequelize.col('records.movement'), 'movement'],
],
group: [sequelize.col('records.movement')],
include: [...parsedFilterInclude],
order: [],
subQuery: false,
raw: true,
} as any;
const records = await ctx.db.getModel('view_records_contracts').findAll(query);
const records = await ctx.db.getModel('records').findAll(query);
const inNum = records.find((item) => item.movement === Movement.in)?.all_price ?? 0;
const outNum = records.find((item) => item.movement === Movement.out)?.all_price ?? 0;
const data = {
@ -195,270 +176,70 @@ export class RecordPreviewController {
@Action('pdf')
async printPreview(ctx: Context) {
// 中间表id盘点单id单双列展示人工/全部/带金额,距上边距,纸张,字体,注解
const {
params: { recordId: id, stockId, isDouble, settingType, margingTop, paper, font, annotate },
params: { recordId, isDouble, settingType, margingTop },
} = ctx.action;
// #region 样式参数
let pdfTop: number;
if (Number(margingTop)) {
pdfTop = Number(margingTop);
} else {
const currentUser = await ctx.db.getRepository('users').findOne({ filter: { id: ctx.state.currentUser.id } });
pdfTop = Number(currentUser?.pdf_top_margin);
}
const isAnnotate = annotate === 'true' ? true : false;
// 拉侧面文字说明
const pdfExplain = await ctx.db.getRepository('basic_configuration').find();
// 租金数据
const leaseData = await ctx.db.sequelize.query(this.sqlLoader.sqlFiles['pdf_record_lease'], {
replacements: {
recordId,
},
type: QueryTypes.SELECT,
});
// 记录单数据
const records = await ctx.db.sequelize.query(this.sqlLoader.sqlFiles['pdf_record'], {
replacements: {
recordId,
},
type: QueryTypes.SELECT,
});
const record = records[0] as any;
// 拉标题
const systemSetting = await this.systemSetting.get();
record.systemTitle = systemSetting?.title || '异常数据,请联系相关负责人!';
const userID = record.createdById || record.updatedById;
const user = await ctx.db.getRepository('users').findOne({ filter: { id: userID } });
record.nickname = user?.nickname || '';
record.userPhone = user?.phone || '';
record.pdfExplain = pdfExplain[0]?.out_of_storage_explain;
if (Number(margingTop)) {
record.margingTop = Number(margingTop);
} else {
// 查询当前用户信息
const currentUser = await ctx.db.getRepository('users').findOne({ filter: { id: ctx.state.currentUser.id } });
record.margingTop = Number(currentUser?.pdf_top_margin) || 0;
}
const double = isDouble === '0' || isDouble === '1' ? isDouble : pdfExplain[0].record_columns;
// 费用数据
const feeData = await ctx.db.sequelize.query(this.sqlLoader.sqlFiles['pdf_record_fee'], {
replacements: {
recordId,
},
type: QueryTypes.SELECT,
});
// 订单打印选项
const printSetup =
settingType === '0' || settingType === '1' || settingType === '2'
? settingType
: pdfExplain[0]?.record_print_setup;
// #endregion 样式参数
const double = isDouble === '0' || isDouble === '1' ? isDouble : pdfExplain[0].record_columns;
// #region 查询视图前执行更新视图用来确定所有产品的相关父级id
const products_view = this.sqlLoader.sqlFiles['products_search_rule_special'];
await ctx.db.sequelize.query(products_view);
// #endregion
// 1.盘点单 只需要 leaseData
// 2.租赁/购销单 需要 leaseData、leaseFeeData、noLeaseProductFeeData、noRuleexcluded
// 3.暂存单 只需要 leaseData
let leaseData = [];
let leaseFeeData = [];
let noLeaseProductFeeData = [];
let noRuleexcluded = [];
let contracts = {};
let intermediate: any;
let baseRecord: any;
if (stockId !== 'undefined') {
// 盘点单
leaseData = await ctx.db.sequelize.query(
`
select
p.id AS products_id,
p2.id AS "parentId",
ri."comment",
p2.name AS "parentName",
p2.name || '[' || p.name || ']' AS "name",
p2.unit,
p2.convertible,
p2.conversion_unit,
p.weight AS products_weight,
p.ratio AS products_ratio,
ri.count,
2 as conversion_logic_id
from record_stock rs
join record_items ri on ri.stock_id = rs.id
join products p on p.id = ri.new_product_id
left join products p2 on p2.id = p."parentId"
where rs.id = :stockId`,
{
replacements: {
stockId,
},
type: QueryTypes.SELECT,
},
);
const record_stock = await ctx.db
.getRepository('record_stock')
.findOne({ where: { id: stockId }, appends: ['project'] });
const userID = record_stock.createdById || record_stock.updatedById || 6; // 记得将盘点用户信息补全
const user = await ctx.db.getRepository('users').findOne({ filter: { id: userID } });
baseRecord = {
number: record_stock.id, // 记得生成单号替换
date: record_stock.date,
pdfExplain: pdfExplain[0]?.out_of_storage_explain,
nickname: user.nickname,
userPhone: user.phone,
systemName: pdfExplain[0]?.name,
plate: record_stock.project.name,
};
} else {
// 租赁/购销/暂存单
// 判断是外部视图查看还是表单中间表查看
let model = 'record_contract';
let isStock = false;
if (id.split('_').length > 1) {
// 视图的情况
model = 'view_records_contracts';
}
intermediate = await ctx.db.getRepository(model).findOne({ filter: { id } }); // 中间表/出入库视图
const recordId = intermediate.record_id; // 订单id
const contractId = intermediate.contract_id; // 合同id
let intermediateId = intermediate.id; // 中间表id
if (id.split('_').length > 1) {
// 视图的情况
intermediateId = parseInt(id.split('_')[1]);
}
baseRecord = await ctx.db.getRepository('records').findOne({
filter: { id: recordId },
appends: [
'vehicles',
'in_stock',
'in_stock.company',
'in_stock.contacts',
'out_stock',
'out_stock.company',
'out_stock.contacts',
],
});
let number;
if (intermediate.number) {
// 视图查看
number = intermediate.number;
} else if (!intermediate.number && model === 'record_contract') {
const view = await ctx.db
.getRepository('view_records_contracts')
.findOne({ filter: { rc_id: intermediateId } });
number = view.number;
}
baseRecord.number = number || baseRecord.number;
// 3.拼接订单的pdf基础数据 用户信息
const userID = baseRecord.createdById || baseRecord.updatedById;
const user = await ctx.db.getRepository('users').findOne({ filter: { id: userID } });
baseRecord.nickname = user?.nickname || '';
baseRecord.userPhone = user?.phone || '';
// 侧面栏提示信息
baseRecord.pdfExplain = pdfExplain[0]?.out_of_storage_explain;
baseRecord.systemName = pdfExplain[0]?.name;
isStock = intermediate.record_category === '3'; // 判断是否为暂存单
if (isStock) {
contracts['record_category'] = '3';
const company = await ctx.db.getRepository('company').findOne({
filter: {
id: intermediate.company_id,
},
appends: ['projects'],
});
contracts['first_party'] = company;
const movement = company.projects.find((item) => item.id === baseRecord.in_stock_id) ? '1' : '-1';
contracts['movement'] = movement;
// 暂存单只需要leaseDate
leaseData = await ctx.db.sequelize.query(
`
select
p.id AS products_id,
p2.id AS "parentId",
ri."comment",
p2.name AS "parentName",
p2.name || '[' || p.name || ']' AS "name",
p2.unit,
p2.convertible,
p2.conversion_unit,
p.weight AS products_weight,
p.ratio AS products_ratio,
ri.count,
2 as conversion_logic_id
from records r
join record_items ri on ri.record_id = r.id
join products p on p.id = ri.new_product_id
left join products p2 on p2.id = p."parentId"
where r.id = :recordId`,
{
replacements: {
recordId,
},
type: QueryTypes.SELECT,
},
);
} else {
contracts = await ctx.db.getRepository('contracts').findOne({
filter: {
id: intermediate.contract_id,
},
appends: ['first_party', 'first_party.projects', 'party_b', 'party_b.projects'],
});
contracts['movement'] = intermediate.movement;
// 租赁/购销
leaseData = await ctx.db.sequelize.query(this.sqlLoader.sqlFiles['pdf_record_product_item'], {
replacements: {
recordId,
contractId,
},
type: QueryTypes.SELECT,
});
// 租金产品赔偿
leaseFeeData = await ctx.db.sequelize.query(this.sqlLoader.sqlFiles['pdf_record_product_fee_item'], {
replacements: {
intermediateId: parseInt(intermediateId),
},
type: QueryTypes.SELECT,
});
// 无关联数据
noLeaseProductFeeData = await ctx.db.sequelize.query(this.sqlLoader.sqlFiles['pdf_record_no_porduct_fees'], {
replacements: {
recordId,
contractId,
},
type: QueryTypes.SELECT,
});
// 比如运费按照出入库量,但是录单存在排除的情况,特殊处理
noRuleexcluded = await ctx.db.sequelize.query(
`
select
rfin.count ,
rfin."comment" ,
rfin.new_fee_product_id,
p2.name || '[' || p.name || ']' as fee_name,
p.custom_name,
rfin.is_excluded
from record_contract rc
join record_fee_items_new rfin on rfin.record_contract_id = rc.id and rfin.is_excluded is true and rfin.new_product_id is null
join products p on p.id = rfin.new_fee_product_id
left join products p2 on p."parentId" = p2.id
where rc.id = :intermediateId
`,
{
replacements: {
intermediateId: parseInt(intermediateId),
},
type: QueryTypes.SELECT,
},
);
}
}
// #endregion
const cache = ctx.app.cacheManager.getCache('@hera/plugin-rental') as Cache;
const key = stringify({
intermediate,
baseRecord,
leaseData,
leaseFeeData,
noLeaseProductFeeData,
noRuleexcluded,
contracts,
settings: { isDouble: double, printSetup, margingTop: pdfTop, paper, font, isAnnotate },
});
const key = stringify({ record, leaseData, feeData, settings: { isDouble: double, printSetup } });
const result = await cache.get(key);
if (result) {
if (Buffer.isBuffer(result)) {
ctx.body = result;
} else {
//@ts-ignore
ctx.body = Buffer.from(result.data);
}
} else {
const buf = await getStream.buffer(
//@ts-ignore
await this.recordPdfService.transformPdfV2(
intermediate,
baseRecord,
leaseData,
leaseFeeData,
noLeaseProductFeeData,
noRuleexcluded,
contracts,
{ isDouble: double, printSetup, margingTop: pdfTop, paper, font, isAnnotate },
),
// @ts-ignore
await this.recordPdfService.transformPdfV2(record, leaseData, feeData, { isDouble: double, printSetup }),
);
ctx.body = buf;
await cache.set(key, buf);

View File

@ -7,7 +7,6 @@ import { QueryTypes } from 'sequelize';
import { Cache } from '@tachybase/cache';
import getStream from 'get-stream';
import { stringify } from 'flatted';
import { SettlementProductsService } from '../services/settlement-products-service';
@Controller('settlements')
export class SettlementController {
@ -20,78 +19,23 @@ export class SettlementController {
@Inject(() => SettlementService)
private settlmentService: SettlementService;
@Inject(() => SettlementProductsService)
private SettlementProductsService: SettlementProductsService;
@Action('calculate')
async updateOrderDetails(ctx: Context) {
const {
params: { settlementsId },
} = ctx.action;
const products_view = this.sqlLoader.sqlFiles['products_search_rule_special'];
await ctx.db.sequelize.query(products_view);
const leaseSql = this.sqlLoader.sqlFiles['settlement_calc_products'];
const feeSql = this.sqlLoader.sqlFiles['settlement_calc_fee_products'];
const feeNoProductSql = this.sqlLoader.sqlFiles['settlement_calc_fee_no_products'];
const recordFeeSql = this.sqlLoader.sqlFiles['settlement_calc_record_fee_products'];
const settlementLeaseData = await ctx.db.sequelize.query(leaseSql, {
const SQL = this.sqlLoader.sqlFiles['settlement_calc'];
const settlement = await ctx.db.sequelize.query(SQL, {
logging: console.log,
raw: true,
plain: true,
replacements: {
settlementsId,
},
type: QueryTypes.SELECT,
});
const settlementFeeData = await ctx.db.sequelize.query(feeSql, {
replacements: {
settlementsId,
},
type: QueryTypes.SELECT,
});
const settlementFeeNoProductData: any = await ctx.db.sequelize.query(feeNoProductSql, {
replacements: {
settlementsId,
},
type: QueryTypes.SELECT,
});
const settlement = await ctx.db.getRepository('settlements').findOne({
where: {
id: settlementsId,
},
fields: ['start_date', 'end_date'],
});
const settlementRecordFee: any = await ctx.db.sequelize.query(recordFeeSql, {
replacements: {
settlementsId,
},
type: QueryTypes.SELECT,
});
for (const item of settlementFeeNoProductData) {
if (item.conversion_logic_id > 4) {
const weightRules = await ctx.db.getRepository('weight_rules').find({
where: {
logic_id: item.conversion_logic_id,
},
appends: ['new_product'],
});
item['weightRules'] = weightRules;
} else {
item['weightRules'] = null;
}
}
const settlementAddItems = await ctx.db.getRepository('settlement_add_items').findOne({
where: {
add_id: settlementsId,
},
});
const settlementAbout = {
settlementLeaseData,
settlementFeeData,
settlementFeeNoProductData,
settlementRecordFee,
settlementAddItems,
start_date: settlement.start_date,
end_date: settlement.end_date,
};
await this.SettlementProductsService.calculate(settlementAbout as any, settlementsId);
await this.settlmentService.calculate(settlement as any, settlementsId);
}
@Action('pdf')

View File

@ -20,20 +20,12 @@ export class WaybillsController {
ctx.body = await renderWaybill(null);
return;
}
const products = await ctx.db.sequelize.query(this.sqlLoader.sqlFiles['waybills_products'], {
replacements: {
recordId: recordId,
},
type: QueryTypes.SELECT,
});
const waybills = await ctx.db.sequelize.query(this.sqlLoader.sqlFiles['pdf_waybills'], {
replacements: {
recordId: recordId,
},
type: QueryTypes.SELECT,
});
const data = waybills[0];
data['products'] = products;
const settings = {};
if (Number(margingTop)) {
settings['margingTop'] = Number(margingTop);
@ -41,7 +33,7 @@ export class WaybillsController {
const currentUser = await ctx.db.getRepository('users').findOne({ filter: { id: ctx.state.currentUser.id } });
settings['margingTop'] = Number(currentUser?.pdf_top_margin) || 0;
}
ctx.body = await renderWaybill(data as Waybill, settings);
ctx.body = await renderWaybill(waybills[0] as Waybill, settings);
}
@Action('group')

View File

@ -8,8 +8,8 @@ export default class extends Migration {
console.log('开始执行脚本record_category_script');
const result: any = await this.app.db.sequelize.query(`
UPDATE records
SET record_category =
CASE
SET record_category =
CASE
WHEN category = '1' AND movement = '1' THEN '2'
WHEN category = '1' AND movement = '-1' THEN '3'
WHEN category = '0' AND movement = '1' THEN '4'

View File

@ -1,10 +1,197 @@
import React from 'react';
import { Document, Page, Text, View, StyleSheet, Image, renderToStream } from '@hera/plugin-core';
import * as QRCode from 'qrcode';
import { RecordCategory } from '../../utils/constants';
import { formatCurrency, formatQuantity } from '../../utils/currencyUtils';
import { dayjs } from '@tachybase/utils';
import { RecordPdfOptions } from '../../interfaces/options';
import { PrintSetup } from '../../utils/system';
const fontSizes = {
title: '13px',
subTitle: '11px',
main: '9px',
side: '8px',
};
const styles = StyleSheet.create({
page: {
flexDirection: 'column',
backgroundColor: '#FFF',
fontFamily: 'source-han-sans',
padding: '12px',
},
title: {
textAlign: 'center',
fontSize: fontSizes.title,
},
subTitle: {
textAlign: 'center',
fontSize: fontSizes.subTitle,
},
content: {
flexDirection: 'row',
marginLeft: '10pt',
marginRight: '10pt',
},
main: {
flex: 1,
},
side: {
width: '11px',
fontSize: fontSizes.side,
paddingLeft: '2px',
},
tableHeader: {
fontSize: fontSizes.main,
flexDirection: 'row',
},
headerLeft: {
width: '205px',
},
headerMiddle: {
flex: 1,
},
headerLeftLeft: {
flex: 2,
},
headerLeftRight: {
flex: 1,
},
headerRight: {
width: '205px',
},
tableContentTitle: {
fontSize: fontSizes.main,
flexDirection: 'row',
},
tableCellLargeTitle: {
flex: 2,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
},
tableCellTitle: {
flex: 1,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
},
tableCell2Title: {
flex: 2,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
},
tableCellTitleLast: {
flex: 1,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderRight: '1px solid black',
marginRight: '-1px',
},
tableCell2TitleLast: {
flex: 2,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderRight: '1px solid black',
marginRight: '-1px',
},
tableContent: {
fontSize: fontSizes.main,
flexDirection: 'row',
},
tableCell: {
flex: 1,
textAlign: 'right',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderBottom: '1px solid black',
},
tableCellNoLeft: {
flex: 1,
textAlign: 'right',
borderTop: '1px solid black',
marginTop: '-1px',
borderBottom: '1px solid black',
},
tableCell2: {
flex: 2,
textAlign: 'right',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderBottom: '1px solid black',
},
tableCellLast: {
flex: 1,
textAlign: 'right',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderRight: '1px solid black',
marginRight: '-1px',
borderBottom: '1px solid black',
},
tableCell2Last: {
flex: 2,
textAlign: 'right',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderRight: '1px solid black',
marginRight: '-1px',
borderBottom: '1px solid black',
},
tableFooter: {
flexDirection: 'row',
},
tableFooterQr: {
width: '50px',
marginLeft: '-50px',
transform: 'translate(50px, 0)',
},
tableFooterLeft: {
flex: 1,
fontSize: fontSizes.main,
borderLeft: '1px solid black',
paddingLeft: '50px',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderBottom: '1px solid black',
marginBottom: '-1px',
},
tableFooterRight: {
flex: 1,
fontSize: fontSizes.main,
border: '1px solid black',
paddingRight: '50px',
margin: '-1px',
},
sign: {
marginTop: '0px',
flexDirection: 'row',
},
signPart: {
flex: 1,
fontSize: fontSizes.main,
},
});
/**
*
* @param isDouble 01
@ -15,223 +202,67 @@ const PreviewDocument = ({
detail,
record,
priceRule,
options,
isDouble,
printSetup,
}: {
imageUrl: string;
detail: any;
record: any[];
priceRule: any[];
options: any; // RecordPdfOptions;
isDouble: Number;
printSetup: String;
}) => {
const fontSizes = {
title: '13px',
subTitle: '11px',
main: `${options.font || 9}px`,
side: '8px',
};
const styles = StyleSheet.create({
page: {
flexDirection: 'column',
backgroundColor: '#FFF',
fontFamily: 'source-han-sans',
padding: '12px',
},
title: {
textAlign: 'center',
fontSize: fontSizes.title,
},
subTitle: {
textAlign: 'center',
fontSize: fontSizes.subTitle,
},
content: {
flexDirection: 'row',
marginLeft: '10pt',
marginRight: '10pt',
},
main: {
flex: 1,
},
side: {
width: '11px',
fontSize: fontSizes.side,
paddingLeft: '2px',
},
tableHeader: {
fontSize: fontSizes.main,
flexDirection: 'row',
},
headerLeft: {
width: '205px',
},
headerMiddle: {
flex: 1,
},
headerLeftLeft: {
flex: 2,
},
headerLeftRight: {
flex: 1,
},
headerRight: {
width: '205px',
},
tableContentTitle: {
fontSize: fontSizes.main,
flexDirection: 'row',
},
tableCellLargeTitle: {
flex: 2,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
},
tableCellTitle: {
flex: 1,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
},
tableCell2Title: {
flex: 2,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
},
tableCellTitleLast: {
flex: 1,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderRight: '1px solid black',
marginRight: '-1px',
},
tableCell2TitleLast: {
flex: 2,
textAlign: 'center',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderRight: '1px solid black',
marginRight: '-1px',
},
tableContent: {
fontSize: fontSizes.main,
flexDirection: 'row',
},
tableCell: {
flex: 1,
textAlign: 'right',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderBottom: '1px solid black',
},
tableCellNoLeft: {
flex: 1,
textAlign: 'right',
borderTop: '1px solid black',
marginTop: '-1px',
borderBottom: '1px solid black',
},
tableCell2: {
flex: 2,
textAlign: 'right',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderBottom: '1px solid black',
},
tableCellLast: {
flex: 1,
textAlign: 'right',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderRight: '1px solid black',
marginRight: '-1px',
borderBottom: '1px solid black',
},
tableCell2Last: {
flex: 2,
textAlign: 'right',
borderLeft: '1px solid black',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderRight: '1px solid black',
marginRight: '-1px',
borderBottom: '1px solid black',
},
tableFooter: {
flexDirection: 'row',
},
tableFooterQr: {
width: '50px',
marginLeft: '-50px',
transform: 'translate(50px, 0)',
},
tableFooterLeft: {
flex: 1,
fontSize: fontSizes.main,
borderLeft: '1px solid black',
paddingLeft: '50px',
marginLeft: '-1px',
borderTop: '1px solid black',
marginTop: '-1px',
borderBottom: '1px solid black',
marginBottom: '-1px',
},
tableFooterRight: {
flex: 1,
fontSize: fontSizes.main,
border: '1px solid black',
paddingRight: '50px',
margin: '-1px',
},
sign: {
marginTop: '0px',
flexDirection: 'row',
},
signPart: {
flex: 1,
fontSize: fontSizes.main,
},
});
const annotate = options.isAnnotate;
if (annotate) {
let commentIndex = 1;
record.forEach((item) => {
if (item.comment) {
item.commentIndex = commentIndex;
commentIndex++;
isDouble = Number(isDouble);
const date = detail.date;
const number = detail.number;
const origin = detail.original_number;
const singlePlayer = detail.nickname + ' ' + detail?.userPhone;
const car = detail.vehicles ? detail.vehicles.map((item) => item?.number).join(' ') : '';
const outOfStorage = detail.movement > 0 ? '入库' : '出库';
const getTitle = () => {
let name;
if (detail.category === RecordCategory.lease) {
name = detail.contract?.project?.associated_company?.name ?? detail.systemTitle;
} else {
if (outOfStorage === '入库') {
name = detail.in_stock?.name ?? detail.systemTitle;
} else {
name = detail.out_stock?.name ?? detail.systemTitle;
}
});
}
const paper = options.paper;
const isDouble = Number(options.isDouble);
const explain = !detail.record_category
? `盘点单用于清算仓库盈亏盈余。`
: `如供需双方未签正式合同,本${
detail.record_category
}${detail.movement} ${
detail.movement === '入库' ? '出库方' : '采购方'
} `;
}
return name;
};
const recordsName = getTitle();
const outOfStorageAddress = (type) => {
// 此type用于展示出入类型
if (type === 1) {
// 返回出库
return detail.out_stock.name;
} else {
// 确定入库
return detail.in_stock.name;
}
};
const projectPhone = detail.contract?.project?.contacts
?.map((item) => (item.name || '') + (item.phone || ''))
.join(' ');
const recordType = {
'0': '租赁',
'1': '购销',
'2': '暂存',
'3': '盘点',
};
const explain =
detail.category === RecordCategory.inventory
? `盘点单用于清算仓库盈亏盈余。`
: `如供需双方未签正式合同,本${
recordType[detail.category]
}${outOfStorage} ${
outOfStorage === '入库' ? '出库方' : '采购方'
} `;
const getAllPrice = () => {
let price = 0;
if (detail.record_category === '购销' && priceRule.filter(Boolean).length) {
if (detail.category === RecordCategory.purchase && priceRule.filter(Boolean).length) {
priceRule.forEach((element) => {
price += element.all_price;
});
@ -239,7 +270,7 @@ const PreviewDocument = ({
return formatCurrency(price, 2);
};
const dobulePriceRule = [];
if (isDouble && detail.record_category === '购销' && priceRule.filter(Boolean).length) {
if (isDouble && detail.category === RecordCategory.purchase && priceRule.filter(Boolean).length) {
// 双列展示
const leftData = priceRule.slice(0, Math.ceil(priceRule.length / 2));
const rightData = priceRule.slice(Math.ceil(priceRule.length / 2));
@ -278,40 +309,26 @@ const PreviewDocument = ({
<View key={index} style={styles.tableContent}>
{columns.map((column, columnIndex) => (
<React.Fragment key={columnIndex}>
<Text style={styles.tableCell2}>
{annotate && item[column + 'commentIndex']
? item[column + 'commentIndex'] + '……' + item[column + 'name']
: item[column + 'name']}
</Text>
<Text style={styles.tableCell2}>{item[column + 'name']}</Text>
<Text style={styles.tableCell}>
{!item[column + 'count'] ? '' : formatQuantity(item[column + 'count'], 2) + item[column + 'unit']}
</Text>
<Text
style={
annotate
? !isDouble || column === 'right_'
? styles.tableCellLast
: styles.tableCell
: styles.tableCell
}
>
<Text style={styles.tableCell}>
{!item[column + 'total']
? ''
: formatQuantity(item[column + 'total'], 2) + item[column + 'conversion_unit']}
</Text>
{!annotate && (
<Text style={!isDouble || column === 'right_' ? styles.tableCellLast : styles.tableCell}>
{item[column + 'isExcluded']
? '不计入合同 ' + (item[column + 'comment'] || '')
: item[column + 'comment'] || ''}
</Text>
)}
<Text style={!isDouble || column === 'right_' ? styles.tableCellLast : styles.tableCell}>
{item[column + 'isExcluded']
? '不计入合同 ' + (item[column + 'comment'] || '')
: item[column + 'comment'] || ''}
</Text>
</React.Fragment>
))}
</View>
));
let addCol = <></>;
if (detail.record_category === '购销') {
if (detail.category === RecordCategory.purchase) {
const allprice = getAllPrice();
addCol = (
<View style={styles.tableContent}>
@ -319,27 +336,15 @@ const PreviewDocument = ({
<React.Fragment key={columnIndex}>
<Text style={styles.tableCell2}></Text>
<Text style={styles.tableCell}>{column === 'left_' ? '' : '总金额'}</Text>
<Text
style={
annotate
? !isDouble || column === 'right_'
? styles.tableCellLast
: styles.tableCell
: styles.tableCell
}
>
{column === 'left_' ? '' : allprice}
</Text>
{!annotate && (
<Text style={!isDouble || column === 'right_' ? styles.tableCellLast : styles.tableCell}></Text>
)}
<Text style={styles.tableCell}>{column === 'left_' ? '' : allprice}</Text>
<Text style={!isDouble || column === 'right_' ? styles.tableCellLast : styles.tableCell}></Text>
</React.Fragment>
))}
</View>
);
}
return detail.record_category === '购销' ? (
return detail.category === RecordCategory.purchase ? (
<>
{page}
{addCol}
@ -376,83 +381,71 @@ const PreviewDocument = ({
));
};
const projectPhone = detail.record_party_b?.contacts?.map((item) => (item.name || '') + (item.phone || '')).join(' ');
const car = detail.vehicles ? detail.vehicles.map((item) => item?.number).join(' ') : '';
return (
<Document>
<Page size={paper || 'A4'} style={{ ...styles.page, marginTop: options.margingTop }}>
{' '}
{/**transform: `scale(${trans})` */}
<Text style={styles.title}>
{!detail.record_category
? detail.plate
: detail.contract_first_party?.name || detail.systemName || '异常数据,请联系相关负责人!'}
</Text>
<Text style={styles.subTitle}>{detail.contract_first_party ? detail.movement : '盘点'}</Text>
<Page size="A4" style={{ ...styles.page, marginTop: detail.margingTop }}>
<Text style={styles.title}>{recordsName}</Text>
<Text style={styles.subTitle}>{detail.category === RecordCategory.inventory ? '盘点' : outOfStorage}</Text>
<View style={styles.content}>
<View style={styles.main}>
{detail.record_category === '租赁' && (
<View>
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{detail.record_party_b.company.name || ''}</Text>
<Text style={styles.headerLeftRight}>
{detail.record_date && dayjs(detail.record_date).format('YYYY-MM-DD')}
</Text>
</View>
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{detail.record_party_b.name || ''}</Text>
<Text style={styles.headerLeftRight}>{detail.record_number}</Text>
</View>
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{detail.record_party_b.address}</Text>
<Text style={styles.headerLeftRight}>{detail.record_origin}</Text>
</View>
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{projectPhone}</Text>
<Text style={styles.headerLeftRight}>{car}</Text>
</View>
{/* 第一行 */}
<View style={styles.tableHeader}>
{detail.category === RecordCategory.purchase && (
<Text style={styles.headerLeft}>{outOfStorageAddress(1)}</Text>
)}
{detail.category === RecordCategory.staging && (
<Text style={styles.headerLeft}>{outOfStorageAddress(1)}</Text>
)}
{detail.category === RecordCategory.inventory && (
<Text style={styles.headerLeft}>{detail.in_stock?.name}</Text>
)}
{detail.category !== RecordCategory.lease && (
<Text style={styles.headerMiddle}>{date && dayjs(date).format('YYYY-MM-DD')}</Text>
)}
{detail.category !== RecordCategory.lease && <Text style={styles.headerRight}>{number}</Text>}
</View>
{/* 第二行 */}
<View style={styles.tableHeader}>
{detail.category === RecordCategory.purchase && (
<Text style={styles.headerLeft}>{outOfStorageAddress(0)}</Text>
)}
{detail.category === RecordCategory.staging && (
<Text style={styles.headerLeft}>{outOfStorageAddress(0)}</Text>
)}
{detail.category !== RecordCategory.inventory && detail.category !== RecordCategory.lease && (
<Text style={styles.headerMiddle}>{car}</Text>
)}
{detail.category !== RecordCategory.inventory && detail.category !== RecordCategory.lease && (
<Text style={styles.headerRight}>{origin}</Text>
)}
</View>
{/* 租赁 */}
{detail.category === RecordCategory.lease && (
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{detail.contract?.project?.company?.name || ''}</Text>
<Text style={styles.headerLeftRight}>{date && dayjs(date).format('YYYY-MM-DD')}</Text>
</View>
)}
{(detail.record_category === '购销' || detail.record_category === '暂存') && (
<View>
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>
{detail.record_category === '暂存' ? '出库方:' : '销售单位:'}
{detail.record_party_b?.company?.name || ''} {detail.record_party_b.name || ''}
</Text>
<Text style={styles.headerMiddle}>
{detail.record_date && dayjs(detail.record_date).format('YYYY-MM-DD')}
</Text>
<Text style={styles.headerLeftRight}>{detail.record_number}</Text>
</View>
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>
{detail.record_category === '暂存' ? '入库方:' : '采购单位:'}
{detail.record_party_a.name || ''}
</Text>
<Text style={styles.headerMiddle}>{car}</Text>
<Text style={styles.headerLeftRight}>{detail.record_origin}</Text>
</View>
{detail.category === RecordCategory.lease && (
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{detail.contract?.project?.name || ''}</Text>
<Text style={styles.headerLeftRight}>{number}</Text>
</View>
)}
{!detail.record_category && (
<View>
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{detail.plate || ''}</Text>
<Text style={styles.headerMiddle}>
{detail.record_date && dayjs(detail.record_date).format('YYYY-MM-DD')}
</Text>
<Text style={styles.headerLeftRight}>{detail.record_number}</Text>
</View>
{detail.category === RecordCategory.lease && (
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{detail.contract?.project?.address}</Text>
<Text style={styles.headerLeftRight}>{origin}</Text>
</View>
)}
{detail.category === RecordCategory.lease && (
<View style={styles.tableHeader}>
<Text style={styles.headerLeftLeft}>{projectPhone}</Text>
<Text style={styles.headerLeftRight}>{car}</Text>
</View>
)}
{/* 定价 */}
{detail.record_category === '购销' && (
{detail.category === RecordCategory.purchase && (
<View style={styles.tableContentTitle}>
<Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
@ -470,20 +463,21 @@ const PreviewDocument = ({
)}
</View>
)}
{detail.record_category === '购销' && renderPrice(isDouble ? dobulePriceRule : priceRule, isDouble)}
{detail.category === RecordCategory.purchase &&
renderPrice(isDouble ? dobulePriceRule : priceRule, isDouble)}
{/* ============================================================两表分割================================================================================= */}
{/* 单列租金及费用 */}
<View style={styles.tableContentTitle}>
<Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
{!annotate && <Text style={!isDouble ? styles.tableCellTitleLast : styles.tableCellTitle}></Text>}
<Text style={!isDouble ? styles.tableCellTitleLast : styles.tableCellTitle}></Text>
{isDouble && (
<>
<Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
<Text style={annotate ? styles.tableCellTitleLast : styles.tableCellTitle}></Text>
{!annotate && <Text style={styles.tableCellTitleLast}></Text>}
<Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitleLast}></Text>
</>
)}
</View>
@ -501,21 +495,14 @@ const PreviewDocument = ({
</Text>
</View>
<View style={styles.sign}>
<Text style={styles.signPart}>{detail.nickname + ' ' + detail?.userPhone}</Text>
{detail.record_category === '租赁' && <Text style={styles.signPart}></Text>}
{detail.record_category === '租赁' && <Text style={styles.signPart}></Text>}
{detail.record_category === '购销' && <Text style={styles.signPart}></Text>}
{detail.record_category === '购销' && <Text style={styles.signPart}></Text>}
{!detail.record_category && <Text style={styles.signPart}></Text>}
<Text style={styles.signPart}>{singlePlayer}</Text>
{detail.category === RecordCategory.lease && <Text style={styles.signPart}></Text>}
{detail.category === RecordCategory.lease && <Text style={styles.signPart}></Text>}
{detail.category === RecordCategory.purchase && <Text style={styles.signPart}></Text>}
{detail.category === RecordCategory.purchase && <Text style={styles.signPart}></Text>}
{detail.category === RecordCategory.staging && <Text style={styles.signPart}></Text>}
{detail.category === RecordCategory.inventory && <Text style={styles.signPart}></Text>}
</View>
{record.map((item) => (
<>
<View style={{ marginTop: '10px' }}></View>
<View style={styles.tableContent}>
{annotate && item.commentIndex && <Text style={{}}>{item.commentIndex + ':' + item.comment}</Text>}
</View>
</>
))}
</View>
<View style={styles.side}>
<Text>{detail.pdfExplain}</Text>
@ -525,7 +512,13 @@ const PreviewDocument = ({
</Document>
);
};
export const renderItV2 = async (rent: { detail: any; record: any[]; priceRule: any[]; options: any }) => {
export const renderItV2 = async (rent: {
detail: any;
record: any[];
priceRule: any[];
isDouble: Number;
printSetup: String;
}) => {
const url = 'https://shcx.daoyoucloud.com/admin';
const imageUrl = await QRCode.toDataURL(url);
return await renderToStream(<PreviewDocument imageUrl={imageUrl} {...rent} />);

View File

@ -222,7 +222,7 @@ const PreviewDocument = ({
<Document>
<Page size="A4" style={styles.page}>
<Text style={styles.title}>
{contracts.first_party?.name ?? `${PromptText.noContractedCompany}`}
{contracts.project?.associated_company?.name ?? `${PromptText.noContractedCompany}`}
</Text>
<Text style={styles.subTitle}></Text>
@ -231,7 +231,7 @@ const PreviewDocument = ({
<View style={styles.tableHeader}>
<Text style={styles.headerLeft}>
{contracts.party_b?.name}
{contracts?.project?.company?.name}
</Text>
<Text style={styles.headerRight}>
@ -259,7 +259,7 @@ const PreviewDocument = ({
{contracts.project?.contacts.map((contact) => contact.name + ' ' + contact.phone).join(' ')}
</Text>
<Text style={styles.headerRight}>{contracts.operator.nickname}</Text>
<Text style={styles.headerRight}>{contracts.operator.nickname}</Text>
</view>
<View style={styles.spacing} />
<View style={styles.tableContentTitle}>
@ -412,23 +412,21 @@ const PreviewDocument = ({
</View>
<View style={styles.tableFooterRight}>
<Text>{contracts.project?.comment}</Text>
<Text>
{contracts.project?.associated_company?.name ?? `${PromptText.noContractedCompany}`}
</Text>
</View>
</View>
<View style={styles.sign}>
<Text style={styles.signPart}>{contracts.party_b?.name} </Text>
<Text style={styles.signPart}>{contracts.first_party?.name}</Text>
<Text style={styles.signPart}> </Text>
<Text style={styles.signPart}></Text>
<Text style={styles.signPart}></Text>
</View>
<View style={styles.sign}>
<Text style={styles.signPart}></Text>
<Text style={styles.signPart}> </Text>
</View>
<View style={styles.sign}>
<Text style={styles.signPart}></Text>
<Text style={styles.signPart}> </Text>
</View>
<View style={styles.sign}>
<Text style={styles.signPart}></Text>
<Text style={styles.signPart}> </Text>
<Text style={styles.signPart}> </Text>
<Text style={styles.signPart}></Text>
</View>
</View>
</View>

View File

@ -1,6 +1,6 @@
import React from 'react';
import { Document, Page, Text, View, StyleSheet, renderToStream } from '@hera/plugin-core';
import { formatQuantity } from '../../utils/currencyUtils';
import { formatCurrency, formatQuantity } from '../../utils/currencyUtils';
import { converDate } from '../../utils/daysUtils';
import { Waybill } from '../../interfaces/waybill';
@ -193,20 +193,23 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an
);
}
// 单号
const recordNumber = waybill.record_number;
const items = waybill.products
? waybill.products.map((item) => {
const convertible = item.convertible;
const data = {
name: item.products_name,
count: item.count,
unit: item.unit,
conversion_unit: convertible ? item.conversion_unit : item.unit,
total: formatQuantity(item.total, 2),
};
return data;
})
: [];
const recordNumber = waybill.record.number;
const items = (
waybill.record.items
? waybill.record.items.map((item) => {
const convertible = item.product.category.convertible;
const data = {
name: item.product.name + '/' + item.product.spec,
count: item.count,
unit: item.product.category.unit,
conversion_unit: convertible ? item.product.category.conversion_unit : item.product.category.unit,
sort: item.product.category.sort * 100000 + item.product.sort,
total: formatQuantity(convertible ? item.count * item.product.ratio : item.count, 2),
};
return data;
})
: []
).sort((a, b) => a.sort - b.sort);
const chunkedArray = Array.from({ length: Math.ceil(items.length / 3) }, (_, i) => {
const chunk = items.slice(i * 3, i * 3 + 3);
@ -341,15 +344,15 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an
</View>
<View style={{ ...styles.tableCell, flex: '2', paddingRight: '1px' }}>
<Text style={styles.textPadding}>
{waybill.payer_company} {waybill.payer_name}
{waybill.payer?.company?.name} {waybill.payer?.name}
</Text>
</View>
<View style={styles.tableCell}>
<Text style={styles.textPadding}>{waybill.payee_account_name}</Text>
<Text style={styles.textPadding}>{waybill.payee_account?.name}</Text>
</View>
<View style={{ ...styles.tableCellLast, flex: '2', paddingRight: '1px' }}>
<Text style={styles.textPadding}>
{waybill.payee_account_bank} {waybill.payee_account_number}
{waybill.payee_account?.bank} {waybill.payee_account?.number}
</Text>
</View>
</View>
@ -365,14 +368,14 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an
<Text style={styles.tableCellTitle}></Text>
<View style={{ ...styles.tableCell3, borderBottom: '1px solid black' }}>
<Text style={styles.textPadding}>
{waybill.shipper_company} {waybill.shipper_name}
{waybill.out_stock?.company?.name} {waybill.out_stock?.name}
</Text>
</View>
<View style={{ ...styles.tableCell, borderBottom: '1px solid black' }}>
<Text style={styles.textPadding}>{waybill.shipper_contact}</Text>
<Text style={styles.textPadding}>{waybill.shipper_contact?.name}</Text>
</View>
<View style={{ ...styles.tableCell, borderBottom: '1px solid black' }}>
<Text style={styles.textPadding}> {waybill.shipper_contact_phone}</Text>
<Text style={styles.textPadding}> {waybill.shipper_contact?.phone}</Text>
</View>
<Text style={styles.tableCellLast}></Text>
</View>
@ -380,7 +383,7 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an
<View style={styles.tableContent}>
<Text style={styles.tableCellTitle}></Text>
<View style={styles.tableCell3}>
<Text style={styles.textPadding}>{waybill.shipper_address}</Text>
<Text style={styles.textPadding}>{waybill.out_stock?.address}</Text>
</View>
<Text style={styles.tableCell}></Text>
<Text style={styles.tableCell}></Text>
@ -393,14 +396,14 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an
</View>
<View style={{ ...styles.tableCell3, borderBottom: '1px solid black' }}>
<Text style={styles.textPadding}>
{waybill.consignee_company} {waybill.consignee_name}
{waybill.in_stock?.company?.name} {waybill.in_stock?.name}
</Text>
</View>
<View style={{ ...styles.tableCell, borderBottom: '1px solid black' }}>
<Text style={styles.textPadding}>{waybill.consignee_contact}</Text>
<Text style={styles.textPadding}>{waybill.consignee_contact?.name}</Text>
</View>
<View style={{ ...styles.tableCell, borderBottom: '1px solid black' }}>
<Text style={styles.textPadding}>{waybill.consignee_contact_phone}</Text>
<Text style={styles.textPadding}>{waybill.consignee_contact?.phone}</Text>
</View>
<Text style={styles.tableCellLast}></Text>
</View>
@ -408,7 +411,7 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an
<View style={styles.tableContent}>
<Text style={styles.tableCellTitle}></Text>
<View style={styles.tableCell3}>
<Text style={styles.textPadding}>{waybill.consignee_address}</Text>
<Text style={styles.textPadding}>{waybill.in_stock?.address}</Text>
</View>
<Text style={styles.tableCell}></Text>
<Text style={styles.tableCell}></Text>
@ -418,7 +421,7 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an
<View style={styles.tableContentPayment}>
<Text style={styles.tableCellTitle}></Text>
<View style={{ ...styles.tableCell3, borderBottom: '1px solid black' }}>
<Text style={styles.textPadding}>{waybill.carrier}</Text>
<Text style={styles.textPadding}>{waybill.carrier?.name}</Text>
</View>
<Text style={{ ...styles.tableCell, borderBottom: '1px solid black' }}></Text>
<Text style={{ ...styles.tableCell, borderBottom: '1px solid black' }}></Text>
@ -428,15 +431,15 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an
<View style={styles.tableContentPayment}>
<Text style={styles.tableCellTitle}></Text>
<View style={styles.tableCell}>
<Text style={styles.textPadding}>{waybill.driver}</Text>
<Text style={styles.textPadding}>{waybill.driver?.name}</Text>
</View>
<Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCell}>{waybill.driver_idcard}</Text>
<Text style={styles.tableCell}>{waybill.driver?.id_card}</Text>
<View style={styles.tableCell}>
<Text style={styles.textPadding}>{waybill.vehicles}</Text>
<Text style={styles.textPadding}>{waybill.record.vehicles.map((item) => item?.number).join(' ')}</Text>
</View>
<View style={styles.tableCell}>
<Text style={styles.textPadding}>{waybill.driver_phone}</Text>
<Text style={styles.textPadding}>{waybill.driver?.phone}</Text>
</View>
<Text style={styles.tableCellLast}></Text>
</View>

View File

@ -59,7 +59,6 @@ export class PluginRentalServer extends Plugin {
await collectionRepo.createMany({
records: categories
.filter(Boolean)
.filter((item) => typeof item.id !== 'undefined')
.map((item) => ({ collectionName, categoryId: item.id })),
});

View File

@ -1,38 +1,19 @@
import Database, { CreateOptions, MagicAttributeModel } from '@tachybase/database';
import { Db, Service } from '@tachybase/utils';
import { Op } from 'sequelize';
@Service()
export class ContractRuleService {
@Db()
private db: Database;
// 合同重复项需要重新是实现
async load() {
// 合同方案规则重复校验
this.db.on('contract_plan_lease_items.beforeSave', this.leaseItemBeforeSave.bind(this));
// 上表最底表的hooks不会作用到合同方案整体的重复判断所以需要contract_plan的hooks
this.db.on('contract_plans.beforeSave', this.contractPlansBeforeSave.bind(this));
}
async leaseItemBeforeSave(model: MagicAttributeModel, options: CreateOptions): Promise<void> {
if (!options.values) return;
// 只适用单个表数据修改
if (options.values.contract_plan?.id && options.values.new_products?.id) {
const where = {
contract_plan_id: options.values.contract_plan.id,
new_products_id: options.values.new_products.id,
};
if (!model.isNewRecord) {
where['id'] = {
[Op.ne]: model.id,
};
}
const data = await this.db.getRepository('contract_plan_lease_items').findOne({
where,
});
if (data) {
throw new Error('租金存在');
}
}
// beforeSave无法获取多对多关联关系的数据
this.db.on('contract_plan_lease_items.beforeCreate', this.contractPlanLeaseItemsBeforeSave.bind(this));
this.db.on('contract_plan_lease_items.beforeUpdate', this.contractPlanLeaseItemsBeforeSave.bind(this));
this.db.on('contract_plan_fee_items.beforeCreate', this.contractPlanFeeItemsBeforeSave.bind(this));
this.db.on('contract_plan_fee_items.beforeUpdate', this.contractPlanFeeItemsBeforeSave.bind(this));
}
/**
@ -43,28 +24,158 @@ export class ContractRuleService {
*/
async contractPlansBeforeSave(model: MagicAttributeModel, options: CreateOptions): Promise<void> {
if (!options.values) return;
const leaseData = options.values.lease_items.map((item) => item.new_products);
const repeatData = this.repeatQuery(leaseData);
const leaseData2 = options.values.lease_items.map((item) => item.products);
const repeatData = this.repeatQuery2(leaseData2);
if (repeatData.length > 0) {
throw new Error('租金规则中的产品重复!' + repeatData.map((item) => item.name));
const products = repeatData.map((item) => item.label).join(',');
throw new Error('租金规则中的产品重复!重复产品:' + products);
}
const feeData = options.values.fee_items.map((item) => item.new_fee_products);
const repeatFeeData = this.repeatQuery(feeData);
if (repeatFeeData.length > 0) {
throw new Error('费用规则中的产品重复!' + repeatFeeData.map((item) => item.name));
const feeData = options.values.fee_items.map((fee) => fee.fee_product);
const feeRepeatData = this.repeatQuery(feeData);
if (feeRepeatData.length > 0) {
const products = feeRepeatData.map((item) => item.label).join(',');
throw new Error('费用规则中的产品重复!重复费用:' + products);
}
}
repeatQuery(data: any[]): any[] {
const repea = [];
for (let i = 0; i < data.length; i++) {
for (let j = i + 1; j < data.length; j++) {
if (data[i].id === data[j].id) {
repea.push(data[i]);
const productFeeRepeatData = [];
for (const index in options.values.lease_items) {
const fees = options.values.lease_items[index].fee_items?.filter(Boolean);
if (!fees) return;
// 租金,费用,租金费用使用同一判断重复方法,需要处理一下租金费用数据结构
const transFee = fees.map((item) => {
if (Object.keys(item).length > 0 && item.fee_product) {
return { ...item, id: item.fee_product.id, raw_category_id: item.fee_product.id };
}
});
const data = this.repeatQuery(transFee);
if (data.length > 0) {
productFeeRepeatData.push({ index, value: data.map((item) => item.fee_product.label).join(',') });
}
}
return repea;
if (productFeeRepeatData.length > 0) {
const tips = [];
for (const iterator of productFeeRepeatData) {
const tip = `${Number(iterator.index) + 1}条租金数据中费用重复添加`;
tips.push(tip);
}
throw new Error(tips.join(''));
}
}
/**
* before seqlizeHooks事件
* @param model
* @param options
* @returns
*/
async contractPlanLeaseItemsBeforeSave(model: MagicAttributeModel, options: CreateOptions): Promise<void> {
if (!options.values?.contract_plan && !options.values?.products) return;
const plan = await this.db.getRepository('contract_plans').findOne({
where: {
id: options.values?.contract_plan?.id || options.values?.contract_plan_id,
},
appends: ['lease_items', 'lease_items.products'],
});
const add = options.values.products;
const productData = plan.lease_items
.filter((item) => item.id !== options.values.id && item.products.length === add.length)
.map((item) => item.products)
.flat();
productData.forEach((item) => {
const isHas = add.find(
(p) =>
(p.id < 99999 && (p.id === item.id || (item.id > 99999 && p.raw_category_id === item.raw_category_id))) ||
(p.id > 99999 && p.raw_category_id === item.raw_category_id),
);
if (isHas) {
throw new Error('方案中存在此产品');
}
});
}
/**
* before seqlizeHooks事件
* @param model
* @param options
* @returns
*/
async contractPlanFeeItemsBeforeSave(model: MagicAttributeModel, options: CreateOptions): Promise<void> {
if (!options.values?.contract_plan || !options.values?.fee_product) return;
const plan = await this.db.getRepository('contract_plans').findOne({
where: {
id: options.values.contract_plan.id,
},
appends: ['lease_items', 'lease_items.products', 'fee_items', 'fee_items.fee_product'],
});
if (options.values.lease_product) {
// 待页面完成确定入参数格式后
// const feeProduct = plan.fee_items.filter((item) => item.lease_item_id === options.values.lease_product.id // 确定options.values.lease_product是否为数组格式)
} else {
const add = options.values.fee_product;
if (!Array.isArray(add)) return;
const feeData = plan.fee_items.filter((item) => !item.lease_item_id).map((item) => item.fee_product);
feeData.forEach((item) => {
const isHas = add.find((p) => p.id === item.id);
if (isHas) {
throw new Error('方案中存在此赔偿项');
}
});
}
}
/**
*
* @param data
* @returns
*/
repeatQuery(data: any[]): any[] {
if (data.filter(Boolean).length > 0) {
const queryData = data.filter(Boolean);
if (queryData.length > 0) {
const arr = [];
for (let index = 0; index < queryData.length; index++) {
const repea = queryData.slice(index + 1).filter((item) => {
if (item) {
return (
item.id === queryData[index].id ||
(item.raw_category_id === queryData[index].raw_category_id &&
(item.id > 99999 || queryData[index].id > 99999))
);
}
});
arr.push(...repea.filter(Boolean));
}
return arr;
} else {
return [];
}
} else {
return [];
}
}
/**
*
*
* @param data
* @returns
*/
repeatQuery2(data: any[]): any[] {
const result = data.reduce((acc, curr) => {
const found = acc.find((subArr) => subArr.length === curr.length);
if (found) {
found.push(...curr);
} else {
acc.push(curr);
}
return acc;
}, []);
const rep = [];
result.forEach((item) => {
const repeatData = this.repeatQuery(item);
if (repeatData.length > 0) {
rep.push(...item);
}
});
return rep;
}
}

View File

@ -33,7 +33,7 @@ export class ContractService {
if (!options.values) return;
if (options.values.settlementTemplate) {
const temp = {
: `MONTH(EOMONTH(TODAY(), -1))`, // 工作流默认
: `MONTH(EOMONTH(TODAY(), -1))`,
: `MONTH(TODAY())`,
: `YEAR(EOMONTH(TODAY(), -1))`,
};

View File

@ -9,315 +9,329 @@ import Database from '@tachybase/database';
export class RecordPdfService {
@Db()
private db: Database;
async transformPdfV2(recordData: any, lease_data: any, fee_data: any, options: RecordPdfOptions) {
const { isDouble, printSetup } = options;
/**
*
* 1.
* 2.
* 3.
* 4. )
* 5.
*/
async transformPdfV2(
intermediate, // 中间表
baseRecord, //订单基础数据
leaseData, // 租金数据
leaseFeeData, // 赔偿数据
noLeaseProductFeeData,
noRuleexcluded,
contracts,
options,
) {
const { printSetup } = options;
const movement = (movement: string) => {
const data = {
'-1': '出库',
'1': '入库',
};
return data[movement];
};
const record_category = (category) => {
const data = {
'1': '购销',
'2': '租赁',
'3': '暂存',
// 非盘点单的情况就是暂存单
};
return data[category];
};
const needRecord = {
record_number: baseRecord.number,
record_date: baseRecord.date,
record_origin: baseRecord?.origin,
record_party_b: movement(contracts.movement) === '出库' ? baseRecord?.in_stock : baseRecord?.out_stock, // 还需要判断第三个合同的情况,确定一下出库入是否合同公司有关
record_party_a: movement(contracts.movement) === '入库' ? baseRecord?.in_stock : baseRecord?.out_stock,
vehicles: baseRecord?.vehicles,
record_category: record_category(contracts.record_category),
contract_first_party: contracts.first_party, //公司信息,甲方,我们
movement: movement(contracts.movement),
pdfExplain: baseRecord.pdfExplain,
nickname: baseRecord.nickname,
userPhone: baseRecord.userPhone,
systemName: baseRecord.systemName,
plate: baseRecord.plate,
};
// !租金数据
const leasePorducts = leaseData.map((leas) => {
const data = {
...leas,
};
let total: number;
const unit = leas.unit;
let conversion_unit = leas.conversion_unit;
if (leas.conversion_logic_id === ConversionLogics.Keep) {
total = leas.count;
conversion_unit = leas.unit;
} else if (
leas.conversion_logic_id === ConversionLogics.Product ||
leas.conversion_logic_id === ConversionLogics.ActualWeight
) {
const ratio = leas.convertible ? leas.products_ratio : 1;
total = leas.count * ratio;
conversion_unit = leas.convertible ? leas.conversion_unit : leas.unit;
} else if (leas.conversion_logic_id === ConversionLogics.ProductWeight) {
total = leas.count * leas.products_weight;
conversion_unit = 'KG';
} else if (leas.conversion_logic_id > 4) {
if (leas.wr_logic_id === ConversionLogics.Keep) {
total = leas.count * leas.wr_weight;
} else if (leas.wr_logic_id === ConversionLogics.Product) {
const ratio = leas.convertible ? leas.products_ratio : 1;
total = leas.count * leas.wr_weight * ratio;
// 直发单不需要打印预览
if (recordData.category === RecordCategory.purchase2lease || recordData.category === RecordCategory.lease2lease)
return;
// 盘点/暂存单使用默认产品表的换算逻辑
if (recordData.category === RecordCategory.staging || recordData.category === RecordCategory.inventory) {
lease_data = lease_data.map((item) => {
const data = {
...item,
};
// 根据产品表换算
data['conversion_logic_id'] = 2;
return data;
});
}
let make_price;
// 购销单计算报价数据
if (recordData.category === RecordCategory.purchase) {
const price_rule = lease_data.map((item) => {
const data = {
name: item.price_label,
unit_price: item.price_price,
comment: item.price_comment,
};
if (!item.conversion_logic_id) return;
if (item.conversion_logic_id === ConversionLogics.Keep) {
data['count'] = item.count;
data['unit'] = item.unit;
} else if (item.conversion_logic_id === ConversionLogics.Product) {
if (item.convertible) {
data['count'] = item.count * item.ratio;
data['unit'] = item.conversion_unit;
} else {
data['count'] = item.count;
data['unit'] = item.unit;
}
} else if (item.conversion_logic_id === ConversionLogics.ProductWeight) {
data['count'] = item.count * item.weight;
data['unit'] = 'KG';
} else if (item.conversion_logic_id === ConversionLogics.ActualWeight) {
const weight = recordData.record_group_weight_items?.find((w) => w.category.id === item.product_category_id);
if (weight) {
data['count'] = weight.weight;
data['unit'] = '吨';
} else {
data['count'] = recordData.weight;
data['unit'] = '吨';
}
} else {
data['unit'] = 'KG';
if (item.wr.conversion_logic_id === ConversionLogics.Keep) {
data['count'] = item.count * item.wr.weight;
} else if (item.wr.conversion_logic_id === ConversionLogics.Product) {
data['count'] = item.count * item.ratio * item.wr.weight;
}
}
conversion_unit = 'KG';
}
data['total'] = total;
data.unit = unit;
data.conversion_unit = conversion_unit;
return data;
});
// !租金赔偿
const leaseProductsFees = leaseFeeData.map((fee) => {
const data = {
products_id: fee.products_id,
name: fee.fee_custom_name || fee.fee_product,
comment: fee.is_excluded ? fee.comment + '_不计入合同' : fee.comment,
};
let count: number;
if (fee.count_source === SourcesType.staff) {
count = fee.fee_count;
} else if (
fee.count_source === SourcesType.inAndOut ||
(fee.count_source === SourcesType.inbound && contracts.movement === '1') ||
(fee.count_source === SourcesType.outbound && contracts.movement === '0')
) {
count = fee.count;
}
let total: number;
let conversion_unit = fee.fee_rule_unit;
if (fee.conversion_logic_id === ConversionLogics.Keep) {
total = count;
conversion_unit = fee.fee_unit;
} else if (
fee.conversion_logic_id === ConversionLogics.Product ||
fee.conversion_logic_id === ConversionLogics.ActualWeight
) {
const ratio = fee.fee_convertible ? fee.fee_ratio : 1;
total = count * ratio;
} else if (fee.conversion_logic_id === ConversionLogics.ProductWeight) {
total = count * fee.fee_weight;
conversion_unit = 'KG';
} else if (fee.conversion_logic_id > 4) {
const ProductWeightRule = leaseData.find((leas) => leas.products_id === fee.products_id);
if (ProductWeightRule.wr_logic_id === ConversionLogics.Keep) {
total = count * ProductWeightRule.wr_weight;
} else if (ProductWeightRule.wr_logic_id === ConversionLogics.Product) {
const ratio = ProductWeightRule.convertible ? ProductWeightRule.products_ratio : 1;
total = count * ProductWeightRule.wr_weight * ratio;
data['all_price'] = data['count'] * data['unit_price'];
return data;
});
const setData = Object.values(
price_rule.reduce((acc, item) => {
if (!item) {
console.error('报价计算出错', price_rule);
return acc;
}
if (!acc[item.name]) {
acc[item.name] = { ...item };
} else {
acc[item.name].count += item.count;
acc[item.name].all_price += item.all_price;
}
return acc;
}, {}),
);
make_price = setData;
}
// !产品
const leaseData = lease_data
.map((lease) => {
const data = {
name: lease.label,
count: lease.count,
unit: lease.unit,
comment: lease.item_comment,
product_id: lease.product_id,
category_id: lease.category_id,
product_category_name: lease.product_category_name,
};
if (lease.conversion_logic_id === ConversionLogics.Keep) {
data['total'] = lease.count;
data['conversion_unit'] = lease.unit;
} else if (lease.conversion_logic_id === ConversionLogics.Product) {
if (lease.convertible) {
data['total'] = lease.count * lease.ratio;
data['conversion_unit'] = lease.conversion_unit;
} else {
data['total'] = lease.count;
data['conversion_unit'] = lease.unit;
}
} else if (lease.conversion_logic_id === ConversionLogics.ProductWeight) {
data['total'] = lease.count * lease.weight;
data['conversion_unit'] = 'KG';
} else if (lease.conversion_logic_id === ConversionLogics.ActualWeight) {
if (lease.convertible) {
data['total'] = lease.count * lease.ratio;
data['conversion_unit'] = lease.conversion_unit;
} else {
data['total'] = lease.count;
data['conversion_unit'] = lease.unit;
}
} else {
data['conversion_unit'] = 'KG';
if (lease.wr?.conversion_logic_id === ConversionLogics.Keep) {
data['total'] = lease.count * lease.wr?.weight;
} else if (lease.wr?.conversion_logic_id === ConversionLogics.Product) {
data['total'] = lease.count * lease.ratio * lease.wr?.weight;
}
}
conversion_unit = 'KG';
return data;
})
.sort((a, b) => a.category_id - b.category_id);
// !产品赔偿
const product_fee =
printSetup === PrintSetup.Manual
? fee_data.filter((item) => item.product_id && item.count_source === SourcesType.staff)
: fee_data.filter((item) => item.product_id);
const transFormProductFee = product_fee.map((item, index) => {
// 购销/暂存/盘点无费用信息
if (
recordData.category === RecordCategory.purchase ||
recordData.category === RecordCategory.staging ||
recordData.category === RecordCategory.inventory
) {
return;
}
if (printSetup === PrintSetup.DisplayAndPrice) {
data['count'] = total;
data['unit'] = conversion_unit;
data['total'] = total * fee.unit_price;
data['conversion_unit'] = '元';
// 人工录入没有数量
if (item.count_source === SourcesType.staff && !item.fee_count) return;
// 出库单,计数为入库量
if (item.record_movement === '-1' && item.count_source === SourcesType.inbound) return;
// 入库单,计数为出库量
if (item.record_movement === '1' && item.count_source === SourcesType.outbound) return;
const data = {
isFee: true,
isExcluded: item.is_excluded,
name: item.custom_name || item.label,
count1: item.count_source === SourcesType.staff ? item.fee_count : item.product_count,
unit: item.unit,
comment: '',
product_id: item.product_id + '.' + (index + 1),
category_id: item.product_category_id,
};
if (item.conversion_logic_id === ConversionLogics.Keep) {
data['total'] = data.count1;
} else if (item.conversion_logic_id === ConversionLogics.Product) {
if (item.convertible) {
data['total'] = data.count1 * item.product_ratio;
} else {
data['total'] = data.count1;
}
} else if (item.conversion_logic_id === ConversionLogics.ProductWeight) {
data['total'] = data.count1 * item.product_weight;
} else if (item.conversion_logic_id === ConversionLogics.ActualWeight) {
data['total'] = item.actual_weight || item.record_weight;
} else {
data['conversion_unit'] = conversion_unit;
data['total'] = total;
if (item.weight_rules?.conversion_logic_id === ConversionLogics.Keep) {
data['total'] = data.count1 * item.weight_rules.weight;
} else if (item.weight_rules?.conversion_logic_id === ConversionLogics.Product) {
data['total'] = data.count1 * item.weight_rules.weight * item.product_ratio;
}
}
data['conversion_unit'] = item.unit;
if (printSetup === PrintSetup.DisplayAndPrice) {
data['count'] = data['total'];
data['total'] = data['total'] * item.unit_price;
data['conversion_unit'] = '元';
}
return data;
});
const rentData = [...leasePorducts, ...leaseProductsFees].sort((a, b) => a.products_id - b.products_id);
// !无关联赔偿 item中
let manualData = [];
const noProductFee = fee_data.filter((item) => !item.product_id && item.count_source === SourcesType.staff);
if (noProductFee.length) {
const itemFee = await this.db.sequelize.query(`
select ri.product_id, ri.count, p.category_id, ri.comment
from records r
left join record_items ri on ri.record_id = r.id and ri.product_id in (${noProductFee.map((item) => item.fee_product_id).join(',')})
join product p on p.id = ri.product_id
where r.id = ${recordData.id}
`);
const result: any = itemFee[0];
manualData = result.map((item) => {
const feeRule = noProductFee.find((f) => f.fee_product_id === item.product_id);
if (!feeRule) return;
const data = {
name: feeRule.custom_name || feeRule.label,
unit: feeRule.unit,
conversion_unit: feeRule.unit,
comment: item.comment || '',
};
if (feeRule.conversion_logic_id === ConversionLogics.Keep) {
data['total'] = item.count;
} else if (feeRule.conversion_logic_id === ConversionLogics.Product) {
data['total'] = feeRule.convertible ? item.count * feeRule.product_ratio : item.count;
} else if (feeRule.conversion_logic_id === ConversionLogics.ProductWeight) {
data['total'] = item.count * feeRule.weight;
} else if (feeRule.conversion_logic_id === ConversionLogics.ActualWeight) {
data['total'] = feeRule.record_weight;
} else {
const weight_rule = item.weight_rules.find(
(l) => l.product_id === item.product_id || l.product - RulesNumber === item.category_id,
);
if (weight_rule && weight_rule.conversion_logic_id === ConversionLogics.Keep) {
return item.count * weight_rule.weight;
} else if (weight_rule && weight_rule.conversion_logic_id === ConversionLogics.Product) {
return item.count * feeRule.product_ratio * weight_rule.weight;
}
}
if (printSetup === PrintSetup.DisplayAndPrice) {
data['count'] = data['total'];
data['total'] = data['total'] * feeRule.unit_price;
data['conversion_unit'] = '元';
}
return data;
});
}
// !无关联赔偿,出入库量
const fee =
printSetup === PrintSetup.Manual
? []
: fee_data.filter((item) => !item.product_id && item.count_source !== SourcesType.staff);
const no_product_fee = fee
.map((item) => {
if (
recordData.category === RecordCategory.purchase ||
recordData.category === RecordCategory.staging ||
recordData.category === RecordCategory.inventory
) {
return;
}
if (item.record_movement === '-1' && item.count_source === SourcesType.inbound) return;
if (item.record_movement === '1' && item.count_source === SourcesType.outbound) return;
const data = {
isFee: true,
name: item.custom_name || item.label,
unit: item.unit,
comment: '',
};
if (item.conversion_logic_id === ConversionLogics.Keep) {
data['total'] = lease_data.reduce((a, b) => a + b.count, 0);
} else if (item.conversion_logic_id === ConversionLogics.Product) {
data['total'] = lease_data.reduce((a, b) => a + (item.convertible ? b.count * b.ratio : b.count), 0);
} else if (item.conversion_logic_id === ConversionLogics.ProductWeight) {
data['total'] = lease_data.reduce((a, b) => a + b.count * b.weight, 0) / 1000;
} else if (item.conversion_logic_id === ConversionLogics.ActualWeight) {
data['total'] = item.record_weight;
} else {
if (item.weight_rules?.length) {
data['total'] = lease_data.reduce((a, b) => {
const weight_rule = item.weight_rules.find(
(l) => l.product_id === b.product_id || l.product - RulesNumber === b.category_id,
);
if (weight_rule && weight_rule.conversion_logic_id === ConversionLogics.Keep) {
return a + b.count * weight_rule.weight;
} else if (weight_rule && weight_rule.conversion_logic_id === ConversionLogics.Product) {
return a + b.count * b.ratio * weight_rule.weight;
}
}, 0);
}
}
data['conversion_unit'] = '吨';
if (printSetup === PrintSetup.DisplayAndPrice) {
data['count'] = data['total'];
data['total'] = data['total'] * item.unit_price;
data['conversion_unit'] = '元';
}
return data;
})
.filter((item) => item && item.total);
const product_fees = transFormProductFee.filter(Boolean);
// 内容(租金+费用)
const product_correlation = [...leaseData, ...product_fees].sort((a, b) => a.product_id - b.product_id);
// 生成小计
const productTotal = {};
leasePorducts.forEach((element) => {
leaseData.forEach((element) => {
if (element) {
productTotal[element.parentId] = {
name: element.parentName + '[小计]',
total: (productTotal[element.parentId]?.total ?? 0) + element.total,
productTotal[element.category_id] = {
name: element.product_category_name + '[小计]',
total: (productTotal[element.category_id]?.total ?? 0) + element?.total,
conversion_unit: element.conversion_unit,
count: '',
unit: '',
isTotal: true,
comment: '',
parentId: element.parentId,
// 报价用
priceName: element.parentName,
price: element.unit_price,
category_id: element.category_id,
};
}
});
// 插入小计
const productTotalItems = Object.entries(productTotal).map(([_, value]) => value);
productTotalItems.forEach((itemB: any) => {
const lastIndex = rentData.map((itemA) => itemA.parentId).lastIndexOf(itemB.parentId);
const lastIndex = product_correlation.map((itemA) => itemA.category_id).lastIndexOf(itemB.category_id);
if (lastIndex !== -1) {
rentData.splice(lastIndex + 1, 0, itemB);
product_correlation.splice(lastIndex + 1, 0, itemB);
} else {
rentData.push(itemB);
product_correlation.push(itemB);
}
});
// !无关联费用(人工录入)
const noproFeesStaff = noLeaseProductFeeData
.filter((item) => item.count_source === SourcesType.staff)
.map((fee) => {
const data = {
fee_products_id: fee.fee_products_id,
name: fee.name,
comment: fee.is_excluded ? fee.comment + '_不计入合同' : fee.comment,
};
let total: number;
let conversion_unit;
if (fee.conversion_logic_id === ConversionLogics.Keep) {
total = fee.count;
conversion_unit = fee.unit;
} else if (
fee.conversion_logic_id === ConversionLogics.Product ||
fee.conversion_logic_id === ConversionLogics.ActualWeight
) {
const ratio = fee.convertible ? fee.ratio : 1;
total = fee.count * ratio;
conversion_unit = fee.convertible ? fee.conversion_unit : fee.unit;
} else if (fee.conversion_logic_id === ConversionLogics.ProductWeight) {
total = fee.count * fee.weight;
conversion_unit = 'KG';
} else if (fee.conversion_logic_id > 4) {
if (fee.wr_logic_id === ConversionLogics.Keep) {
total = fee.count * fee.wr_weight;
} else if (fee.wr_logic_id === ConversionLogics.Product) {
const ratio = fee.convertible ? fee.ratio : 1;
total = fee.count * fee.wr_weight * ratio;
}
conversion_unit = 'KG';
}
if (printSetup === PrintSetup.DisplayAndPrice) {
data['count'] = total;
data['unit'] = conversion_unit;
data['total'] = total * fee.unit_price;
data['conversion_unit'] = '元';
} else {
data['conversion_unit'] = conversion_unit;
data['total'] = total;
}
return data;
});
rentData.push(...noproFeesStaff);
// !无关联费用(出入库量)
if (printSetup === PrintSetup.Display || printSetup === PrintSetup.DisplayAndPrice) {
const noproFees = noLeaseProductFeeData
.filter((item) => item.count_source !== SourcesType.staff)
.map((fee) => {
const data = {
name: fee.custom_name || fee.name,
};
if (
fee.count_source === SourcesType.inAndOut ||
(fee.count_source === SourcesType.inbound && intermediate.movement === '1') ||
(fee.count_source === SourcesType.outbound && intermediate.movement === '-1')
) {
let total = 0;
let conversion_unit: string;
leaseData.forEach((ele) => {
if (fee.conversion_logic_id === ConversionLogics.Keep) {
total += ele.count;
conversion_unit = ele.unit;
} else if (fee.conversion_logic_id === ConversionLogics.Product) {
const ratio = ele.convertible ? ele.ratio : 1;
total += ele.count * ratio;
conversion_unit = ele.convertible ? ele.conversion_unit : ele.unit;
} else if (fee.count_logic_id === ConversionLogics.ActualWeight) {
total = baseRecord.weight;
conversion_unit = '吨';
} else if (fee.conversion_logic_id === ConversionLogics.ProductWeight) {
total += ele.count * ele.weight;
conversion_unit = 'KG';
} else if (fee.conversion_logic_id > 4) {
const weightRule = fee.weight_rules.find((item) => item.new_product_id === ele.products_id);
if (weightRule) {
if (weightRule.conversion_logic_id === ConversionLogics.Keep) {
total += ele.count * weightRule.weight;
} else if (weightRule.conversion_logic_id === ConversionLogics.Product) {
const ratio = weightRule.convertible ? weightRule.ratio : 1;
total += ele.count * weightRule.weight * ratio;
}
conversion_unit = 'KG';
}
}
});
data['isFee'] = true;
conversion_unit = fee.fee_rule_unit || conversion_unit;
if (printSetup === PrintSetup.DisplayAndPrice) {
data['count'] = total;
data['unit'] = conversion_unit;
data['total'] = total * fee.unit_price;
data['conversion_unit'] = '元';
} else {
data['conversion_unit'] = conversion_unit;
data['total'] = total;
}
return data;
}
})
.filter(Boolean);
rentData.push(...noproFees);
const excluded = noRuleexcluded
.map((item) => {
const data = {
name: item.custom_name || item.fee_name,
total: item.count,
comment: item.is_excluded ? (item.comment || '') + ' 不计入合同' : item.comment || '',
conversion_unit: '',
};
return data;
})
.filter(Boolean);
rentData.push(...excluded);
}
// 报价
let priceRule = [];
if (needRecord.record_category === '购销') {
priceRule = productTotalItems.map((item: any) => {
return {
name: item.priceName,
unit_price: item.price,
unit: item.conversion_unit,
count: item.total,
all_price: item.total * item.price,
comment: item.comment,
};
});
}
// 注意不记录合同
const recordPdfData = [...product_correlation, ...manualData, ...no_product_fee].filter(Boolean);
return await renderItV2({
detail: needRecord,
record: rentData,
priceRule,
options,
detail: recordData,
record: recordPdfData,
priceRule: make_price,
isDouble,
printSetup,
});
}
}

View File

@ -1,20 +1,63 @@
import Database, { CreateOptions, MagicAttributeModel } from '@tachybase/database';
import Database, { CreateOptions, MagicAttributeModel, Transaction } from '@tachybase/database';
import { Db, Service } from '@tachybase/utils';
import { ConversionLogics, RecordCategory, settlementStatus } from '../../utils/constants';
import { ConversionLogics, Movement, RecordCategory, RecordTypes, settlementStatus } from '../../utils/constants';
import validateLicensePlate from '../../utils/validateLIcensePlate';
import { QueryTypes } from 'sequelize';
import _ from 'lodash';
@Service()
export class RecordService {
@Db()
private db: Database;
async load() {
this.db.on('records.afterSave', this.recordsAfterSave.bind(this));
this.db.on('records.beforeSave', this._calcAllPrice.bind(this));
// 盘点单
this.db.on('records.afterSaveWithAssociations', this._createStock.bind(this));
}
async load() {
// 1. 创建项目/更新项目( 直发单,生成对应租赁/购销,出入库单数据, 并且设置订单多对多project字段关系
this.db.on('records.afterCreate', this.afterCreateDirectRecord.bind(this));
this.db.on('records.afterUpdate', this.afterUpdateDirectRecord.bind(this));
this.db.on('records.afterSave', this.recordsAfterSave.bind(this));
}
/**
*
*/
async afterCreateDirectRecord(model: MagicAttributeModel, options: CreateOptions): Promise<void> {
const { values, transaction, context } = options;
if (!values) {
return;
}
if (values.record_category === RecordTypes.purchaseDirect || values.record_category === RecordTypes.rentDirect) {
await this._createRecord(model, values, transaction, context, null);
}
}
/**
*
*/
async afterUpdateDirectRecord(model: MagicAttributeModel, options: CreateOptions): Promise<void> {
const { values, transaction, context } = options;
if (!values) {
return;
}
// 运输单的创建会走订单的update如果存在waybill一定是在录运输单
if (values.waybill) return;
if (values.record_category === RecordTypes.purchaseDirect || values.record_category === RecordTypes.rentDirect) {
const deleteDatas = await this.db.getRepository('records').find({ where: { direct_record_id: model.id } });
// 删除订单多对多项目表数据
const records = await this.db.getRepository('records').find({ where: { direct_record_id: model.id } });
await this._deleteReocrdProject(
records.map((item) => item.id),
transaction,
);
// 删除新建时创建的订单
await this.db.sequelize.query(
`
delete from records
where records.direct_record_id = ${model.id}
`,
{
transaction,
},
);
const numbers = deleteDatas.map((item) => item.number);
await this._createRecord(model, values, transaction, context, numbers);
}
}
/**
* afterSave hooks
* @param model
@ -23,11 +66,173 @@ export class RecordService {
async recordsAfterSave(model: MagicAttributeModel, options: CreateOptions): Promise<void> {
// 运输单导致的订单更新不必走以下订单逻辑减少性能消耗有waybill一定是运输单录入
if (options.values?.waybill) return;
// 订单新建更新后(根据合同确定出入库字段)
await this._setProject(model, options);
// 订单发生变化时更新对应结算单的状态(需要重新计算)
await this._updateSettlementStatus(model, options);
// 车牌号校验
await this._checkPlateNumber(model, options);
}
/**
*
*/
async _checkPlateNumber(record: MagicAttributeModel, options: CreateOptions) {
const { transaction, context } = options;
if (record.dataValues?.vehicles) {
const ids = record.dataValues.vehicles.filter((item) => typeof item === 'number').join(',');
const vehicles = await this.db.sequelize.query(`select * from vehicles where id in (${ids})`, {
transaction,
});
if (vehicles[0].length) {
vehicles[0].forEach((item: any) => {
if (item.number) {
const validate = validateLicensePlate(item.number);
if (!validate) {
throw new Error('车牌号格式错误');
}
}
});
}
}
}
// 新版订单录入界面,手动赋值对应的出入库信息
async _setProject(record: MagicAttributeModel, options: CreateOptions) {
const { values, transaction, context } = options;
// 非新增和修改订单信息的
if (!values || values.record_category === undefined) {
return;
}
// 暂存和盘点是不需要处理的
if (values.category === RecordCategory.inventory) {
// values.category === RecordCategory.staging ||
return;
}
const where = {};
// 订单导入直接取值
if (values.import && values.in_stock && values.out_stock) {
where['in_stock_id'] = values.in_stock.id;
where['out_stock_id'] = values.out_stock.id;
} else {
let out_stock, in_stock;
if (values.record_category === RecordTypes.purchaseDirect || values.record_category === RecordTypes.rentDirect) {
// 采购直发/租赁直发
const inContract = values.in_contract;
const contractProject = await this.db
.getRepository('project')
.findOne({ where: { id: inContract.project_id } });
in_stock = contractProject.dataValues;
if (values.record_category === RecordTypes.rentDirect) {
const project = await this.db
.getRepository('project')
.findOne({ where: { id: values.out_contract.project_id } });
out_stock = project.dataValues;
} else {
out_stock = values.out_stock;
}
} else if (
values.record_category === RecordTypes.rentInStock ||
values.record_category === RecordTypes.rentOutStock
) {
if (!values.contract) return;
const contract = values.contract;
const contractProject = await this.db.getRepository('project').findOne({ where: { id: contract.project_id } });
// 租赁出库/租赁入库,确定出入库
// out_stock, in_stock
// 租赁入库, out_stock = 合同中project_id, in_stock = 合同中项目的associated_company_id => 项目表id
const associatedCompanyProject = await this.db
.getRepository('project')
.findOne({ where: { company_id: contractProject.associated_company_id, category: '1' } });
if (values.record_category === RecordTypes.rentInStock) {
out_stock = contractProject.dataValues;
in_stock = associatedCompanyProject?.dataValues;
} else {
out_stock = associatedCompanyProject?.dataValues;
in_stock = contractProject.dataValues;
}
} else if (
values.record_category === RecordTypes.purchaseInStock ||
values.record_category === RecordTypes.sellOutStock
) {
out_stock = values.out_stock;
const associatedCompanyProject = await this.db
.getRepository('project')
.findOne({ where: { company_id: out_stock.company_id } });
in_stock = associatedCompanyProject;
} else if (values.category === RecordCategory.staging) {
in_stock = values.in_stock;
out_stock = values.out_stock;
}
if (in_stock) {
where['in_stock_id'] = in_stock.id;
}
if (out_stock) {
where['out_stock_id'] = out_stock.id;
}
}
await record.update(where, { transaction });
if (where['in_stock_id'] && where['out_stock_id']) {
// 设置完出库入,设置项目
await this._updateRecordProjects(record, values, transaction, where);
}
}
/**
* hook赋值方法
* @param model
* @param values
* @param transaction
* @param where
*/
async _updateRecordProjects(model, values, transaction, where) {
if (
values.category === RecordCategory.purchase2lease ||
values.category === RecordCategory.lease2lease ||
values.category === RecordCategory.staging
) {
await this._deleteReocrdProject([model.id], transaction);
await this.db.sequelize.query(
`
INSERT INTO record_projects (project_id, record_id)
SELECT ${where.in_stock_id} AS project_id, ${model.id} AS record_id
UNION ALL
SELECT ${where.out_stock_id} AS project_id, ${model.id} AS project_id;
`,
{ transaction },
);
} else {
await this._deleteReocrdProject([model.id], transaction);
await this.db.sequelize.query(
`
INSERT INTO record_projects (project_id, record_id)
SELECT
CASE WHEN movement = '1' THEN ${where.out_stock_id} ELSE ${where.in_stock_id} END AS project_id,
id AS record_id
FROM records
WHERE records.id = ${model.id}
`,
{ transaction },
);
}
}
/**
*
* @param id
* @param transaction
*/
async _deleteReocrdProject(id: number[], transaction: Transaction) {
await this.db.getRepository('record_projects').destroy({
transaction,
filter: {
record_id: {
$eq: id,
},
},
});
}
/**
*
*/
@ -60,177 +265,235 @@ export class RecordService {
}
}
}
}
/**
*
*/
async _checkPlateNumber(record: MagicAttributeModel, options: CreateOptions) {
const { transaction, context } = options;
if (record.dataValues?.vehicles) {
const ids = record.dataValues.vehicles.filter((item) => typeof item === 'number').join(',');
const vehicles = await this.db.sequelize.query(`select * from vehicles where id in (${ids})`, {
transaction,
});
if (vehicles[0].length) {
vehicles[0].forEach((item: any) => {
if (item.number) {
const validate = validateLicensePlate(item.number);
if (!validate) {
throw new Error('车牌号格式错误');
}
}
});
if (values?.category === RecordCategory.purchase) {
// 定价
const rule = values.price_items;
// 产品
const products = values.items;
// 分组实际总量
const weight_items = values.group_weight_items;
let allPrice = 0;
for (const item of rule) {
const data = await this._amountCalculation(item, products, weight_items, values.weight);
allPrice += data;
}
}
}
/**
*
*/
async _calcAllPrice(record: MagicAttributeModel, options: CreateOptions) {
const { values } = options;
if (!values) return;
if (values.new_contracts.length) {
const contracts = values.new_contracts.map((item) => item.contract).find((i) => i.record_category === '1');
if (contracts && values.items && values.items?.length > 0) {
const leaseRule = await this.db.getRepository('contract_plan_lease_items').find({
const recordId = record.id;
// 更新订单总金额字段
await this.db.getModel('records').update(
{ all_price: allPrice },
{
where: {
contract_id: contracts.id,
id: recordId,
},
appends: ['conversion_logic', 'conversion_logic.weight_items'],
});
const sql = `
WITH RECURSIVE tree1 AS (
SELECT id, "parentId"
FROM products
WHERE id = :dataId
UNION ALL
SELECT p.id, p."parentId"
FROM tree1 up
JOIN products p ON up."parentId" = p.id
)
select id
from tree1
`;
let allPrice = 0;
for (const item of values.items) {
const treeIds = await this.db.sequelize.query(sql, {
replacements: {
dataId: item.new_product.id,
},
type: QueryTypes.SELECT,
});
const rule = leaseRule.find((rule) => treeIds.find((i: any) => i.id === rule.new_products_id));
if (rule) {
const price = rule.unit_price || 0;
const count = item.count || 0;
if (rule.conversion_logic_id === ConversionLogics.Keep) {
allPrice += price * count;
} else if (
rule.conversion_logic_id === ConversionLogics.Product ||
rule.conversion_logic_id === ConversionLogics.ActualWeight
) {
const ratio = item.new_product.parent?.convertible ? item.new_product.ratio : 1;
allPrice += price * count * ratio;
} else if (rule.conversion_logic_id === ConversionLogics.ProductWeight) {
allPrice += price * count * (item.new_product.weight || 0);
} else {
const weightRule = rule.conversion_logic.weight_items.find((weight) =>
treeIds.find((i: any) => i.id === weight.new_product_id),
);
if (weightRule.conversion_logic_id === ConversionLogics.Keep) {
allPrice += price * count * weightRule.weight;
} else if (weightRule.conversion_logic_id === ConversionLogics.Product) {
const ratio = item.new_product.parent?.convertible ? item.new_product.ratio : 1;
allPrice += price * count * ratio * weightRule.weight;
}
}
}
}
if (values.all_price !== allPrice) {
record.all_price = allPrice;
}
}
transaction,
},
);
}
}
/**
*
*
* @param rule
* @param products
* @param weight_items
* @returns
*/
async _createStock(record: MagicAttributeModel, options: CreateOptions) {
const { values, transaction, context } = options;
if (!values) return;
const record_contract = record.get('new_contracts').filter((item) => typeof item === 'object');
if (record_contract.length) {
const stockProduct = await this.db.getRepository('products').find({
private async _amountCalculation(rule, products, weight_items, recprdWeight) {
const calc_products = products.filter(
(product) => product.product.category_id === rule.product.id - 99999 || product.product.id === rule.product.id,
);
const categoryIds = calc_products.map((item) => item.product.category_id).filter(Boolean);
const categoryIdsStr = categoryIds.join(',');
const datas = await this.db.sequelize.query(`
SELECT pc.*
FROM product_category pc
WHERE pc.id IN (${categoryIdsStr ? categoryIdsStr : 'null'});
`);
// 相关分类数据
const categoryDatas = datas[0];
if (!categoryDatas.length) return;
if (rule.conversion_logic.id === ConversionLogics.Keep) {
const allPrice = calc_products.reduce(
(accumulator, currentValue) => accumulator + currentValue.count * rule.unit_price,
0,
);
return allPrice;
} else if (rule.conversion_logic.id === ConversionLogics.Product) {
const allPrice = calc_products.reduce((accumulator, currentValue) => {
const category: any = categoryDatas.find((item: any) => currentValue.product.category_id === item.id);
if (category.convertible) {
return accumulator + currentValue.count * currentValue.product.ratio * rule.unit_price;
} else {
return accumulator + currentValue.count * rule.unit_price;
}
}, 0);
return allPrice;
} else if (rule.conversion_logic.id === ConversionLogics.ProductWeight) {
const allPrice = calc_products.reduce(
(accumulator, currentValue) => accumulator + currentValue.count * currentValue.product.weight * rule.unit_price,
0,
);
return allPrice / 1000;
} else if (rule.conversion_logic.id === ConversionLogics.ActualWeight) {
// 根据产品找分组实际重量
const weightDate =
weight_items.find((item) => item.products?.find((product) => product?.id === rule.product.id - 99999))
?.weight || recprdWeight;
if (weightDate) {
const allPrice = weightDate * rule.unit_price;
return allPrice;
} else {
const allPrice = calc_products.reduce((accumulator, currentValue) => {
const category: any = categoryDatas.find((item: any) => currentValue.product.category_id === item.id);
if (category.convertible) {
return accumulator + currentValue.count * currentValue.product.ratio * rule.unit_price;
} else {
return accumulator + currentValue.count * rule.unit_price;
}
}, 0);
return allPrice;
}
} else {
const ids = calc_products
.map((item) => item.product.category_id)
.filter(Boolean)
.join(',');
const rules = this.db.sequelize.query(`
select wr.*
from weight_rules wr
where wr.product_id IN (${ids}) and wr.logic_id = ${rule.conversion_logic.id}
`);
const weight_rules = rules[0];
const allPrice = calc_products.reduce((accumulator, currentValue) => {
const weight_rule: any = weight_rules?.find((item: any) => currentValue.product_id === item.id);
const category: any = categoryDatas.find((item: any) => currentValue.product.category_id === item.id);
if (category.convertible && weight_rule?.logic_id === ConversionLogics.Keep) {
return accumulator + currentValue.count * currentValue.product.ratio * rule.unit_price;
} else if (category.convertible && weight_rule?.logic_id === ConversionLogics.Product) {
return accumulator + currentValue.count * currentValue.product.ratio * weight_rule.weight * rule.unit_price;
} else if (!category.convertible && weight_rule?.logic_id === ConversionLogics.Keep) {
return accumulator + currentValue.count * rule.unit_price;
} else {
return accumulator + currentValue.count * weight_rule?.weight * rule.unit_price;
}
}, 0);
return allPrice || 0;
}
}
/**
*
*/
async _createRecord(model, values, transaction, context, numbers) {
delete values.number;
delete values.id;
values.vehicles?.forEach((item) => delete item.record_vehicles);
//采购直发单
if (values.record_category === RecordTypes.purchaseDirect && values.category === RecordCategory.purchase2lease) {
const inProject = await this.db.getModel('project').findOne({
where: {
id: values.in_contract.project_id,
},
});
const base_project = await this.db.getModel('project').findOne({
where: {
company_id: inProject.associated_company_id, // 签约公司id
category: '1',
},
});
const sql = `
WITH RECURSIVE tree1 AS (
SELECT id, "parentId"
FROM products
WHERE id = :dataId
UNION ALL
SELECT p.id, p."parentId"
FROM tree1 up
JOIN products p ON up."parentId" = p.id
)
select id
from tree1
`;
for (const i of record_contract) {
const item = i.dataValues;
const stock = {
record_contract_id: item.id,
items: [],
project_id: item.contract.dataValues.project_id,
};
if (!item.fees) return;
const stockItems = await Promise.all(
item.fees.map(async (fee) => {
if (typeof fee !== 'object') return;
const treeIds = await this.db.sequelize.query(sql, {
replacements: {
dataId: fee.dataValues.new_fee_product.id,
},
type: QueryTypes.SELECT,
});
// 两数组ID取并集
const isStockProduct = _.intersectionBy(stockProduct, treeIds, 'id');
if (isStockProduct.length > 0) {
// 这里一般是无关联产品的费用,有赔偿标记的类别为需要找对应的产品
// 这里需要平替找到对应的产品,目前没有这个分类的产品
const parent = await this.db.getRepository('products').find({
where: {
id: fee.dataValues.new_fee_product.parentId,
},
});
const fee_products = await this.db.getRepository('products').find({
where: {
name: fee.dataValues.new_fee_product.name,
},
appends: ['parent'],
});
const pro = fee_products.find((pro) => pro.parent?.name === parent.name);
return { new_product: pro || fee.dataValues.new_fee_product, count: fee.count };
} else {
if (fee.dataValues.new_product) {
return { new_product: fee.dataValues.new_product, count: fee.count };
}
}
}),
);
if (stockItems.filter(Boolean).length > 0) {
stock.items = stockItems;
await this.db.getRepository('record_stock').updateOrCreate({
filterKeys: ['record_contract_id'],
values: stock,
transaction,
});
}
//1. 创建购销入库单
const purchaseData = {
...values,
direct_record_id: model.id,
};
purchaseData['record_category'] = RecordTypes.purchaseInStock;
purchaseData['movement'] = Movement.in;
purchaseData['category'] = RecordCategory.purchase;
purchaseData['in_stock'] = base_project.dataValues;
if (numbers?.[0]) {
purchaseData['number'] = numbers[0];
}
purchaseData.items.forEach((element) => {
delete element.record_id;
delete element.id;
});
await this.db.getRepository('records').create({ values: purchaseData, transaction, context });
// 2. 创建租赁出库单
const leaseData = {
...values,
direct_record_id: model.id,
};
leaseData['record_category'] = RecordTypes.rentOutStock;
leaseData['movement'] = Movement.out;
leaseData['category'] = RecordCategory.lease;
leaseData['contract'] = values.in_contract;
leaseData['out_stock'] = base_project.dataValues;
leaseData['in_stock'] = inProject.dataValues;
if (numbers?.[1]) {
leaseData['number'] = numbers[1];
}
leaseData.items.forEach((element) => {
delete element.record_id;
delete element.id;
});
await this.db.getRepository('records').create({ values: leaseData, transaction, context });
}
if (values.record_category === RecordTypes.rentDirect && values.category === RecordCategory.lease2lease) {
// 1.创建租赁入库
const leaseInData = {
...values,
direct_record_id: model.id,
};
const outProject = await this.db.getModel('project').findOne({
where: {
id: values.out_contract.project_id,
},
});
const inProject = await this.db.getModel('project').findOne({
where: {
id: values.in_contract.project_id,
},
});
const baseProject = await this.db.getModel('project').findOne({
where: {
company_id: inProject.associated_company_id, // 签约公司id
category: '1',
},
});
leaseInData['record_category'] = RecordTypes.rentInStock;
leaseInData['movement'] = Movement.in;
leaseInData['category'] = RecordCategory.lease;
leaseInData['contract'] = values.out_contract;
leaseInData['out_stock'] = outProject.dataValues;
leaseInData['in_stock'] = baseProject.dataValues;
if (numbers?.[0]) {
leaseInData['number'] = numbers[0];
}
leaseInData.items.forEach((element) => {
delete element.record_id;
delete element.id;
});
await this.db.getRepository('records').create({ values: leaseInData, transaction, context });
// 2.创建租赁出库单
const leaseOutData = {
...values,
direct_record_id: model.id,
};
leaseOutData['record_category'] = RecordTypes.rentOutStock;
leaseOutData['movement'] = Movement.out;
leaseOutData['category'] = RecordCategory.lease;
leaseOutData['contract'] = values.in_contract;
leaseOutData['out_stock'] = baseProject.dataValues;
leaseOutData['in_stock'] = inProject.dataValues;
if (numbers?.[1]) {
leaseOutData['number'] = numbers[1];
}
leaseOutData.items.forEach((element) => {
delete element.record_id;
delete element.id;
});
await this.db.getRepository('records').create({ values: leaseOutData, transaction, context });
}
}
}

View File

@ -887,8 +887,8 @@ export class SettlementService {
});
calc.list.forEach((value) => {
const productDate = converDate(value.date, 'YYYY-MM-DD');
if ((value.name as string)?.includes('-')) {
const name = value.name?.split('-')[0];
if ((value.name as string).includes('-')) {
const name = value.name.split('-')[0];
const valueItem = calc.list.filter(
(item) => converDate(item.date, 'YYYY-MM-DD') === productDate && item.name === name,
)[0];
@ -896,8 +896,8 @@ export class SettlementService {
value.date = dayjs(valueItem.date).add(1, 'seconds');
}
}
if (value.name?.includes('&&')) {
value.name = value.name.replace('&&', '');
if (value.name.includes('$$')) {
value.name = value.name.replace('$$', '');
}
});
calc.list?.sort((a, b) => {

View File

@ -0,0 +1,120 @@
SELECT
r.*,
-- ================================出入库信息================================
(
SELECT
TO_JSONB(
JSONB_SET(
TO_JSONB(project),
'{associated_company}',
COALESCE(TO_JSONB(c2), '{}'::jsonb)
)
)
FROM
project
LEFT JOIN company c2 ON project.associated_company_id = c2.id
WHERE
project.id = r.out_stock_id
) AS out_stock,
-- 查订单入库方
(
SELECT
TO_JSONB(
JSONB_SET(
TO_JSONB(project),
'{associated_company}',
COALESCE(TO_JSONB(c), '{}'::jsonb)
)
)
FROM
project
LEFT JOIN company c ON project.associated_company_id = c.id
WHERE
project.id = r.in_stock_id
) AS in_stock,
TO_JSONB(
-- 项目/客户数据(只有租赁才有此项目,其他项目就是出入库数据)
JSONB_SET(
TO_JSONB(c),
'{project}',
(
SELECT
COALESCE(
TO_JSONB(
JSONB_SET(
TO_JSONB(p3),
'{associated_company}',
CASE
WHEN p3.associated_company_id IS NOT NULL THEN (
SELECT
COALESCE(TO_JSONB(c), '{}'::jsonb)
FROM
company c
WHERE
p3.associated_company_id = c.id
)
ELSE '{}'::jsonb
END
) || JSONB_SET(
TO_JSONB(p3),
'{company}',
(
SELECT
TO_JSONB(c)
FROM
company c
WHERE
p3.company_id = c.id
)
) || JSONB_SET(
TO_JSONB(p3),
'{contacts}',
(
SELECT
COALESCE(JSONB_AGG(c), '[]'::jsonb)
FROM
project_contacts pc
JOIN contacts c ON pc.contact_id = c.id
WHERE
p3.id = pc.project_id
)
)
),
'{}'::jsonb
)
FROM
project p3
WHERE
c.project_id = p3.id
)
)
) AS contract,
-- ==========================================车号==========================================
(
SELECT
COALESCE(JSONB_AGG(v), '[]'::jsonb)
FROM
record_vehicles rv
JOIN vehicles v ON rv.vehicle_id = v.id
WHERE
r.id = rv.record_id
) AS vehicles,
-- 查询分组实际重量
(
SELECT
JSONB_AGG(
JSONB_SET(TO_JSONB(rgwi), '{category}', TO_JSONB(pc))
)
FROM
record_group_weight_items rgwi
JOIN record_group_weight_items_products rgwip ON rgwip.item_id = rgwi.id
JOIN product_category pc ON rgwip.product_category_id = pc.id
WHERE
rgwi.record_id = r.id
) AS record_group_weight_items
FROM
records r
-- 合同数据(租赁有合同)
LEFT JOIN contracts c ON r.contract_id = c.id
WHERE
r.id = :recordId

View File

@ -0,0 +1,122 @@
SELECT
r.movement AS record_movement,
r.weight AS record_weight,
NULL AS actual_weight,
NULL AS product_id,
NULL AS product_count,
NULL AS fee_count,
NULL AS is_excluded,
cpfi.fee_product_id,
p.*,
p.weight AS product_weight,
NULL AS product_ratio,
NULL AS convertible,
NULL AS product_category_id,
cpfi.unit_price,
cpfi.conversion_logic_id,
cpfi.unit,
cpfi.count_source,
cpfi."comment",
cpfi.id,
(
SELECT
JSONB_AGG(wr)
FROM
weight_rules wr
WHERE
wr.logic_id = cpfi.conversion_logic_id
) AS weight_rules
FROM
records r
LEFT JOIN contracts c ON r.contract_id = c.id
LEFT JOIN contract_items ci ON c.id = ci.contract_id
AND ci.start_date <= r.date
AND ci.end_date >= r."date"
LEFT JOIN contract_plans cp ON ci.contract_plan_id = cp.id
LEFT JOIN contract_plan_fee_items cpfi ON cp.id = cpfi.contract_plan_id --lease_item_id证明是关联产品的费用
JOIN product p ON p.id = cpfi.fee_product_id
LEFT JOIN product_category pc3 ON p.category_id = pc3.id
JOIN product_category pc ON pc.id = p.category_id
JOIN unit_conversion_logics ucl ON ucl.id = cpfi.conversion_logic_id
WHERE
r.id = :recordId
GROUP BY
cpfi.id,
p.id,
r.id
UNION
SELECT
r2.movement AS record_movement,
r2.weight AS record_weight,
rwi.weight AS actual_weight,
ri.product_id,
ri.count AS product_count,
rifi.count AS fee_count,
rifi.is_excluded AS is_excluded, -- 不计入合同
cpfi2.fee_product_id,
p1.*,
p2.weight AS product_weight,
p2.ratio AS product_ratio,
pc2.convertible AS convertible,
pc2.id AS product_category_id,
cpfi2.unit_price,
cpfi2.conversion_logic_id,
cpfi2.unit,
cpfi2.count_source,
cpfi2."comment",
cpfi2.id,
(
SELECT
TO_JSONB(wr)
FROM
weight_rules wr
WHERE
ucl2.id > 4
AND wr.logic_id = ucl2.id
AND wr.product_id = ri.product_id
LIMIT
1
) AS weight_rules
FROM
records r2
LEFT JOIN contracts c2 ON r2.contract_id = c2.id
LEFT JOIN contract_items ci2 ON c2.id = ci2.contract_id
AND ci2.start_date <= r2.date
AND ci2.end_date >= r2."date"
LEFT JOIN contract_plans cp2 ON ci2.contract_plan_id = cp2.id
LEFT JOIN contract_plan_lease_items cpli ON cp2.id = cpli.contract_plan_id -- 查租金
JOIN contract_plan_lease_items_products cplip ON cplip.lease_item_id = cpli.id -- 查租金产品
JOIN contract_plan_fee_items cpfi2 ON cpfi2.lease_item_id = cpli.id
AND cpfi2.count_source = '0' -- 查费用
JOIN unit_conversion_logics ucl2 ON ucl2.id = cpfi2.conversion_logic_id -- 查计算规则
JOIN record_items ri ON ri.record_id = r2.id
AND (
ri.product_id = cplip.product_id
OR (
SELECT
p.category_id
FROM
product p
WHERE
p.id = ri.product_id
) = cplip.product_id - 99999
)
LEFT JOIN (
SELECT
record_group_weight_items.id,
COMMENT,
weight,
record_id,
product_category_id
FROM
record_group_weight_items
JOIN record_group_weight_items_products ON record_group_weight_items.id = record_group_weight_items_products.item_id
) rwi ON r2.id = rwi.record_id
AND rwi.product_category_id = ri.product_id
LEFT JOIN record_item_fee_items rifi ON ri.id = rifi.record_item_id
AND rifi.product_id = cpfi2.fee_product_id
JOIN product p1 ON p1.id = cpfi2.fee_product_id
LEFT JOIN product p2 ON ri.product_id = p2.id
LEFT JOIN product_category pc2 ON p2.category_id = pc2.id
WHERE
r2.id = :recordId

View File

@ -0,0 +1,112 @@
-- 租金
-- distinct说明sql根据订单中产品去合同规则中找规则如果合同存在单个产品多条规则会连接查询出多条规则所以需要distinct
-- 直接disinct掉不会有任何影响因为查询数据只关注点单产品数量根换算逻辑正好多条规则换算逻辑是一样的
SELECT DISTINCT
r.category,
ri.comment AS item_comment,
ri.count,
ri.product_id,
p.label,
p.ratio,
p.weight,
p.category_id,
pc.name AS product_category_name,
pc.unit,
pc.convertible,
pc.conversion_unit,
lr.unit_price AS price_price,
vp."label" AS price_label,
lr."comment" AS price_comment,
rwi.product_category_id,
CASE
WHEN r.category = '0' THEN cpli.conversion_logic_id
ELSE lr.conversion_logic_id
END AS conversion_logic_id,
CASE
WHEN ucl.id > 4
AND c.id IS NOT NULL THEN (
SELECT
TO_JSONB(wr)
FROM
weight_rules wr
WHERE
ucl.id = wr.logic_id
AND (
wr.product_id = p.id
OR wr.product_id = pc.id + 99999
)
)
WHEN lr.conversion_logic_id > 4
AND c.id IS NULL THEN (
SELECT
TO_JSONB(wr2)
FROM
weight_rules wr2
WHERE
lr.conversion_logic_id = wr2.logic_id
AND (
wr2.product_id = p.id
OR wr2.product_id = pc.id + 99999
)
)
END AS wr
FROM
records r
LEFT JOIN record_items ri ON r.id = ri.record_id
LEFT JOIN product p ON ri.product_id = p.id
LEFT JOIN product_category pc ON p.category_id = pc.id
---- 租赁找合同规则
LEFT JOIN contracts c ON r.contract_id = c.id
LEFT JOIN contract_items ci ON c.id = ci.contract_id
AND ci.start_date <= r.date
AND ci.end_date >= r."date"
LEFT JOIN contract_plans cp ON ci.contract_plan_id = cp.id
LEFT JOIN contract_plan_lease_items cpli ON cp.id = cpli.contract_plan_id
LEFT JOIN unit_conversion_logics ucl ON cpli.conversion_logic_id = ucl.id
AND cpli.conversion_logic_id > 4
------ 购销找租金定价
LEFT JOIN lease_rules lr ON r.id = lr.record_id
AND (
lr.product_id = p.id
OR lr.product_id = pc.id + 99999
)
LEFT JOIN view_products vp ON vp.id = lr.product_id
LEFT JOIN (
SELECT
record_group_weight_items.id,
COMMENT,
weight,
record_id,
product_category_id
FROM
record_group_weight_items
JOIN record_group_weight_items_products ON record_group_weight_items.id = record_group_weight_items_products.item_id
) rwi ON r.id = rwi.record_id
AND rwi.product_category_id = pc.id
WHERE
r.id = :recordId
AND (
CASE
WHEN c.id IS NOT NULL THEN (
ri.product_id = (
SELECT
cplip.product_id
FROM
contract_plan_lease_items_products cplip
WHERE
cplip.lease_item_id = cpli.id
AND cplip.product_id = p.id
)
OR pc.id + 99999 = (
SELECT
cplip.product_id
FROM
contract_plan_lease_items_products cplip
WHERE
cplip.lease_item_id = cpli.id
AND cplip.product_id - 99999 = pc.id
)
)
ELSE TRUE
END
)

View File

@ -1,64 +1,114 @@
SELECT
r.number record_number,
w.arrival_date, -- 到货日期
w.off_date, -- 承运日期
w.weight_or_amount, -- 吨/趟
w.unit_price, -- 单价
w.additional_cost, -- 附加金额
w.pay_date, -- 付款日期
w.comment,
p."name" AS payer_name, -- 付款方
c."name" AS payer_company, -- 付款方公司
a."name" AS payee_account_name, -- 收款账户
a."number" AS payee_account_number, -- 收款账户
a.bank AS payee_account_bank, -- 收款名称
p2.address AS shipper_address, -- 发货方
p2."name" AS shipper_name, -- 发货方单位
c3.name AS shipper_company, -- 发货方单位
c4."name" AS shipper_contact, -- 发货方联系人
c4.phone AS shipper_contact_phone, -- 发货方联系人
c2."name" AS consignee_contact, -- 收款人
p3.name AS consignee_name, -- 收货方单位
p3.address AS consignee_address,
c5."name" AS consignee_company, -- 收货方单位
c6."name" AS consignee_contact, -- 收货人
c6.phone AS consignee_contact_phone,
c7.name AS carrier, -- 承运商
c8.name AS driver, -- 驾驶员
c8.phone AS driver_phone,
c8.id_card AS driver_idcard, --
(
SELECT
STRING_AGG(v.number::TEXT, ', ')
FROM
record_vehicles rv
JOIN vehicles v ON rv.vehicle_id = v.id
WHERE
r.id = rv.record_id
) AS vehicles,
(
SELECT
waybills_explain
FROM
basic_configuration
) side_information
) side_information,
w.*,
TO_JSONB(
JSONB_SET(
TO_JSONB(payer),
'{company}',
(
SELECT
TO_JSONB(c2)
FROM
company c2
WHERE
payer.company_id = c2.id
)
)
) AS payer, -- 付款方, 查公司company_id
TO_JSONB(payee_account) AS payee_account, -- 收款方账号
TO_JSONB(shipper_contact) AS shipper_contact, -- 发货方联系人
TO_JSONB(consignee_contact) AS consignee_contact, -- 收货方联系人
TO_JSONB(carrier) AS carrier, -- 承运商
TO_JSONB(driver) AS driver, --
TO_JSONB(
JSONB_SET(
TO_JSONB(in_stock),
'{company}',
(
SELECT
TO_JSONB(c)
FROM
company c
WHERE
in_stock.company_id = c.id
)
)
) AS in_stock,
TO_JSONB(
JSONB_SET(
TO_JSONB(out_stock),
'{company}',
(
SELECT
TO_JSONB(c)
FROM
company c
WHERE
out_stock.company_id = c.id
)
)
) AS out_stock,
TO_JSONB(records.*) || JSONB_BUILD_OBJECT(
'items',
(
SELECT
JSONB_AGG(
TO_JSONB(record_items) || JSONB_BUILD_OBJECT(
'product',
TO_JSONB(product.*) || JSONB_BUILD_OBJECT('category', TO_JSONB(product_category.*))
)
)
FROM
record_items
LEFT JOIN product ON record_items.product_id = product.id
LEFT JOIN product_category ON product.category_id = product_category.id
WHERE
records.id = record_items.record_id
),
'vehicles',
(
SELECT
COALESCE(JSONB_AGG(v), '[]'::jsonb)
FROM
record_vehicles rv
JOIN vehicles v ON rv.vehicle_id = v.id
WHERE
records.id = rv.record_id
)
) AS record
FROM
waybills w
JOIN records r ON r.id = w.record_id
LEFT JOIN record_contract rc ON r.id = rc.record_id
LEFT JOIN project p ON w.payer_id = p.id
LEFT JOIN company c ON p.company_id = c.id
LEFT JOIN account a ON w.payee_account_id = a.id
LEFT JOIN contacts c2 ON w.consignee_contact_id = c2.id
JOIN project p2 ON r.out_stock_id = p2.id -- 发货方
LEFT JOIN company c3 ON p2.company_id = c3.id
LEFT JOIN contacts c4 ON w.shipper_contact_id = c4.id
JOIN project p3 ON r.in_stock_id = p3.id -- 收货方
LEFT JOIN company c5 ON p3.company_id = c5.id
LEFT JOIN contacts c6 ON w.consignee_contact_id = c6.id
LEFT JOIN company c7 ON w.carrier_id = c7.id
LEFT JOIN contacts c8 ON w.driver_id = c8.id
LEFT JOIN
-- 付款方
project payer ON w.payer_id = payer.id
LEFT JOIN
-- 收款方账户
account payee_account ON w.payee_account_id = payee_account.id
LEFT JOIN
-- 发货方联系人
contacts shipper_contact ON w.shipper_contact_id = shipper_contact.id
LEFT JOIN
-- 收货方联系人
contacts consignee_contact ON w.consignee_contact_id = consignee_contact.id
LEFT JOIN
-- 承运商
company carrier ON w.carrier_id = carrier.id
LEFT JOIN
-- 司机
contacts driver ON w.driver_id = driver.id
JOIN
-- 订单信息
records ON w.record_id = records.id
JOIN record_items ON record_items.record_id = w.record_id
JOIN product ON product.id = record_items.product_id
JOIN product_category ON product_category.id = product.category_id
LEFT JOIN record_vehicles ON records.id = record_vehicles.record_id
LEFT JOIN project in_stock ON records.in_stock_id = in_stock.id
LEFT JOIN project out_stock ON records.out_stock_id = out_stock.id
WHERE
w.id = :recordId
LIMIT
1 --rc数量不为1

View File

@ -0,0 +1,276 @@
SELECT
s.*,
TO_JSONB(
JSONB_SET(
TO_JSONB(c),
'{rule_items}',
(
SELECT
JSONB_AGG(
JSONB_SET(
TO_JSONB(ci),
'{rule}',
TO_JSONB(
-- 租金
JSONB_SET(
TO_JSONB(cp),
'{lease_items}',
(
SELECT
JSONB_AGG(
JSONB_SET(
TO_JSONB(cpli),
'{ucl}',
TO_JSONB(
JSONB_SET(
TO_JSONB(ucl),
'{weight_items}',
(
SELECT
COALESCE(JSONB_AGG(wr), '[]'::jsonb)
FROM
weight_rules wr
WHERE
ucl.id = wr.logic_id
AND ucl.id > 4
)
)
)
) || JSONB_SET(
TO_JSONB(cpli),
'{product_fee}',
(
SELECT
COALESCE(
JSONB_AGG(
JSONB_SET(TO_JSONB(cpfi), '{product}', TO_JSONB(product)) || JSONB_SET(
TO_JSONB(cpfi),
'{weight_items}',
(
SELECT
COALESCE(JSONB_AGG(wrs), '[]'::jsonb)
FROM
weight_rules wrs
WHERE
wrs.logic_id = cpfi.conversion_logic_id
AND cpfi.conversion_logic_id > 4
)
)
),
'[]'::jsonb
)
FROM
contract_plan_fee_items cpfi
JOIN product ON cpfi.fee_product_id = product.id
WHERE
cpfi.lease_item_id = cpli.id
)
) || JSONB_SET(
TO_JSONB(cpli),
'{products}',
(
SELECT
JSONB_AGG(cplip)
FROM
contract_plan_lease_items_products cplip
WHERE
cplip.lease_item_id = cpli.id
)
)
)
FROM
contract_plan_lease_items cpli
JOIN unit_conversion_logics ucl ON ucl.id = cpli.conversion_logic_id
WHERE
cpli.contract_plan_id = cp.id
)
) ||
-- 费用
JSONB_SET(
TO_JSONB(cp),
'{fee_item}',
(
SELECT
COALESCE(
JSONB_AGG(
JSONB_SET(
TO_JSONB(cpfi),
'{ucl}',
TO_JSONB(
JSONB_SET(
TO_JSONB(ucl),
'{weight_items}',
(
SELECT
COALESCE(JSONB_AGG(wr), '[]'::jsonb)
FROM
weight_rules wr
WHERE
cpfi.conversion_logic_id = wr.logic_id
AND cpfi.conversion_logic_id > 4
)
)
)
) || JSONB_SET(
TO_JSONB(cpfi),
'{product}',
(
SELECT
TO_JSONB(product)
FROM
product
WHERE
cpfi.fee_product_id = product.id
)
)
),
'[]'::jsonb
)
FROM
contract_plan_fee_items cpfi
JOIN unit_conversion_logics ucl ON ucl.id = cpfi.conversion_logic_id
WHERE
cpfi.contract_plan_id = cp.id
)
)
)
)
)
FROM
contract_items ci
LEFT JOIN contract_plans cp ON ci.contract_plan_id = cp.id
WHERE
ci.contract_id = COALESCE(main.id, c.id)
AND ci.start_date IS NOT NULL
AND ci.end_date IS NOT NULL
)
)
) AS contracts,
(
SELECT
JSONB_AGG(
JSONB_SET(
TO_JSONB(r),
'{record_items}',
(
SELECT
COALESCE(
JSONB_AGG(
JSONB_SET(
TO_JSONB(ri),
'{product}',
-- 产品
(
SELECT
TO_JSONB(
JSONB_SET(
TO_JSONB(p),
'{product_category}',
-- 产品分类
TO_JSONB(pc)
)
)
FROM
product p
JOIN product_category pc ON p.category_id = pc.id
WHERE
p.id = ri.product_id
)
) || JSONB_SET(
TO_JSONB(ri),
'{record_item_fee_items}',
(
SELECT
COALESCE(
JSONB_AGG(
JSONB_SET(TO_JSONB(rifi), '{product}', TO_JSONB(p))
),
'[]'::jsonb
)
FROM
record_item_fee_items rifi
JOIN product p ON rifi.product_id = p.id
WHERE
rifi.record_item_id = ri.id
)
)
),
'[]'::jsonb
)
FROM
record_items ri
WHERE
ri.record_id = r.id
)
) || JSONB_SET(
-- 维修赔偿(无产品关联,如:运费)
TO_JSONB(r),
'{fee_item}',
(
SELECT
COALESCE(
JSONB_AGG(
JSONB_SET(TO_JSONB(rfi), '{product}', TO_JSONB(p))
),
'[]'::jsonb
)
FROM
record_fee_items rfi
JOIN product p ON rfi.product_id = p.id
WHERE
rfi.record_id = r.id
)
) || JSONB_SET(
TO_JSONB(r),
'{weight_items}',
(
SELECT
COALESCE(JSONB_AGG(rwi), '[]'::jsonb)
FROM
(
SELECT
record_group_weight_items.id,
COMMENT,
weight,
record_id,
product_category_id
FROM
record_group_weight_items
JOIN record_group_weight_items_products ON record_group_weight_items.id = record_group_weight_items_products.item_id
) rwi
WHERE
rwi.record_id = r.id
)
)
)
FROM
records r
WHERE
r.contract_id = COALESCE(main.id, c.id)
AND r.category = '0'
AND (s.end_date + INTERVAL '1 day - 1 millisecond') >= r.date
) AS records,
(
SELECT
COALESCE(JSONB_AGG(sai), '[]'::jsonb)
FROM
settlement_add_items sai
WHERE
s.id = sai.add_id
) AS settlement_add_items,
(
SELECT
JSONB_AGG(s1)
FROM
settlements s1
WHERE
s1.contract_id = c.id
) AS settlements
FROM
settlements s
JOIN
-- 合同 一对一
contracts c ON c.id = s.contract_id
LEFT JOIN contracts main ON main.id = c.alternative_contract_id
WHERE
s.id = :settlementsId

View File

@ -48,28 +48,6 @@ SELECT
WHERE
contracts."updatedById" = u.id
)
) || JSONB_SET(
TO_JSONB(contracts),
'{first_party}',
(
SELECT
TO_JSONB(c)
FROM
company c
WHERE
c.id = contracts.first_party_id
)
) || JSONB_SET(
TO_JSONB(contracts),
'{party_b}',
(
SELECT
TO_JSONB(c)
FROM
company c
WHERE
c.id = contracts.party_b_id
)
)
) AS contracts,
(

View File

@ -84,6 +84,18 @@ export enum SourcesType {
* 3
*/
inAndOut = '3',
/**
* 4
*/
outboundNumber = '4',
/**
* 5
*/
inboundNumber = '5',
/**
* 6
*/
inAndOutNumber = '6',
}
export enum PromptText {