feat: 仓库二期,出入库单打印预览,产品范围,计价数量,换算数量

Reviewed-on: daoyoucloud/tachycode#503
Co-authored-by: hello@lv <2256334253@qq.com>
Co-committed-by: hello@lv <2256334253@qq.com>
This commit is contained in:
hello@lv 2024-03-26 23:09:59 +08:00 committed by sealday
parent 6251551ded
commit eb090572c6
9 changed files with 362 additions and 315 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@hera/plugin-core", "name": "@hera/plugin-core",
"version": "1.6.1", "version": "1.6.2",
"displayName": "Hera platform", "displayName": "Hera platform",
"displayName.zh-CN": "赫拉平台", "displayName.zh-CN": "赫拉平台",
"description": "Hera platform as nocobase plugin.", "description": "Hera platform as nocobase plugin.",

View File

@ -1,6 +1,6 @@
{ {
"name": "@hera/plugin-rental", "name": "@hera/plugin-rental",
"version": "1.6.2", "version": "1.6.3",
"main": "dist/server/index.js", "main": "dist/server/index.js",
"displayName": "professional construction materials rental system - customized based on hera", "displayName": "professional construction materials rental system - customized based on hera",
"displayName.zh-CN": "专业建材租赁系统 - 基于赫拉定制", "displayName.zh-CN": "专业建材租赁系统 - 基于赫拉定制",

View File

@ -7,6 +7,8 @@ import { Spin } from 'antd';
import { useCachedRequest } from '../hooks'; import { useCachedRequest } from '../hooks';
export const RecordItemCount = observer((props) => { export const RecordItemCount = observer((props) => {
const form = useForm();
const field = useField();
const param = { const param = {
resource: 'product_category', resource: 'product_category',
action: 'list', action: 'list',
@ -15,19 +17,17 @@ export const RecordItemCount = observer((props) => {
}, },
}; };
const { loading, data } = useCachedRequest<any>(param); const { loading, data } = useCachedRequest<any>(param);
const form = useForm();
const field = useField();
if (!data && loading) { if (!data && loading) {
return <Spin />; return <Spin />;
} }
const item = form.getValuesIn(field.path.slice(0, -2).entire); const item = form.getValuesIn(field.path.slice(0, -2).entire);
if (item?.product && item?.count && data) { if (item?.product && item?.count) {
const category = data.data.find((category) => category.id === item.product.category_id); const category = data.data?.find((category) => category.id === item?.product.category_id);
if (!category) return; if (!category) return;
const value = category.convertible ? (item.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; const unit = category.convertible ? category.conversion_unit : category.unit || '';
return <span>{formatQuantity(value, 2) + unit}</span>; return <span>{formatQuantity(value, 2) + unit}</span>;
} }
return <span> - </span>; return <span> - </span>;

View File

@ -4,7 +4,7 @@ import { observer, useField, useForm } from '@formily/react';
import _ from 'lodash'; import _ from 'lodash';
import { ConversionLogics, RecordCategory } from '../../utils/constants'; import { ConversionLogics, RecordCategory } from '../../utils/constants';
import { formatQuantity } from '../../utils/currencyUtils'; import { formatQuantity } from '../../utils/currencyUtils';
import { useCachedRequest, useLeaseItems } from '../hooks'; import { useCachedRequest, useLeaseItems, useProductFeeItems } from '../hooks';
// 计价数量 // 计价数量
export const RecordItemValuationQuantity = observer((props) => { export const RecordItemValuationQuantity = observer((props) => {
@ -33,6 +33,9 @@ export const RecordItemValuationQuantity = observer((props) => {
const { data: leaseItems } = useLeaseItems(contractPlanId); const { data: leaseItems } = useLeaseItems(contractPlanId);
const { data: inLeaseItems } = useLeaseItems(inContractPlanId); const { data: inLeaseItems } = useLeaseItems(inContractPlanId);
const { data: outLeaseItems } = useLeaseItems(outContractPlanId); 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); const item = form.getValuesIn(field.path.slice(0, -2).entire);
if (item?.product && item?.count) { if (item?.product && item?.count) {
@ -50,7 +53,13 @@ export const RecordItemValuationQuantity = observer((props) => {
(product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id, (product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id,
), ),
); );
const count = subtotal(rule, item, productCategory, reqWeightRules); 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 }); count && result.push({ label: '合同', value: count });
} }
// 采购 // 采购
@ -78,12 +87,22 @@ export const RecordItemValuationQuantity = observer((props) => {
const count = subtotal(rule, item, productCategory, reqWeightRules); const count = subtotal(rule, item, productCategory, reqWeightRules);
count && result.push({ label: '报价', value: count }); count && result.push({ label: '报价', value: count });
} }
const leaseRule = inLeaseItems.data.find((leaseItem) => const leaseRule = inLeaseItems.data.find((leaseItem) =>
leaseItem.products.find( leaseItem.products.find(
(product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id, (product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id,
), ),
); );
const count = subtotal(leaseRule, item, productCategory, reqWeightRules); 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 }); count && result.push({ label: '入库合同', value: count });
} }
// 租赁直发 // 租赁直发
@ -93,14 +112,32 @@ export const RecordItemValuationQuantity = observer((props) => {
(product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id, (product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id,
), ),
); );
const count_out = subtotal(contractPlain_out, item, productCategory, reqWeightRules); 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 }); count_out && result.push({ label: '出库合同', value: count_out });
const contractPlain_in = inLeaseItems.data.find((leaseItem) => const contractPlain_in = inLeaseItems.data.find((leaseItem) =>
leaseItem.products.find( leaseItem.products.find(
(product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id, (product) => product.id - 99999 === item.product?.category_id || product.id === item.product?.id,
), ),
); );
const count_in = subtotal(contractPlain_in, item, productCategory, reqWeightRules); 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 }); count_in && result.push({ label: '入库合同', value: count_in });
} }
} }
@ -119,17 +156,17 @@ RecordItemValuationQuantity.displayName = 'RecordItemValuationQuantity';
RecordItemValuationQuantity.__componentType = CustomComponentType.CUSTOM_FIELD; RecordItemValuationQuantity.__componentType = CustomComponentType.CUSTOM_FIELD;
RecordItemValuationQuantity.__componentLabel = '记录单 - 明细 - 计价数量'; 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 count: number;
let unit: string; let unit: string;
if (rule?.conversion_logic_id === ConversionLogics.Keep) { if (rule?.conversion_logic_id === ConversionLogics.Keep) {
count = itemData.count; count = itemData.count;
unit = productCategory.unit; unit = item ? rule.unit : productCategory.unit;
} else if (rule?.conversion_logic_id === ConversionLogics.Product) { } else if (rule?.conversion_logic_id === ConversionLogics.Product) {
count = productCategory.convertible ? itemData.count * itemData.product.ratio : itemData.count; count = productCategory.convertible ? itemData.count * itemData.product.ratio : itemData.count;
unit = productCategory.convertible ? productCategory.conversion_unit : productCategory.unit; unit = item ? rule.unit : productCategory.convertible ? productCategory.conversion_unit : productCategory.unit;
} else if (rule?.conversion_logic_id === ConversionLogics.ProductWeight) { } else if (rule?.conversion_logic_id === ConversionLogics.ProductWeight) {
count = (itemData.count * itemData.product.weight) / 1000; count = item ? itemData.count : (itemData.count * itemData.product.weight) / 1000;
unit = '吨'; unit = '吨';
} else if (rule?.conversion_logic_id === ConversionLogics.ActualWeight) { } else if (rule?.conversion_logic_id === ConversionLogics.ActualWeight) {
count = 0; count = 0;

View File

@ -4,7 +4,7 @@ import React from 'react';
import { observer, useForm } from '@formily/react'; import { observer, useForm } from '@formily/react';
import { RecordCategory } from '../../utils/constants'; import { RecordCategory } from '../../utils/constants';
import { CustomComponentType, CustomFC } from '@hera/plugin-core/client'; import { CustomComponentType, CustomFC } from '@hera/plugin-core/client';
import { useCachedRequest, useLeaseItems } from '../hooks'; import { useCachedRequest, useLeaseItems, useProductFeeItems } from '../hooks';
import { useDeepCompareEffect } from 'ahooks'; import { useDeepCompareEffect } from 'ahooks';
export const RecordProductScope = observer(() => { export const RecordProductScope = observer(() => {
@ -15,36 +15,75 @@ export const RecordProductScope = observer(() => {
let required = { price: false, contract: false, inContract: false, outContract: false }; let required = { price: false, contract: false, inContract: false, outContract: false };
const { data, loading } = useCachedRequest<any>({ const { data, loading } = useCachedRequest<any>({
resource: 'product_category', resource: 'product',
action: 'list', action: 'list',
params: { params: {
pageSize: 99999, pageSize: 99999,
}, },
}); });
const { data: leaseItems, loading: leaseItemsLoading } = useLeaseItems(contractPlanId); const { data: leaseItems, loading: leaseItemsLoading } = useLeaseItems(contractPlanId);
const { data: feeseItems, loading: feeItemsLoading } = useProductFeeItems(contractPlanId);
const { data: inLeaseItems, loading: inLeaseItemsLoading } = useLeaseItems(inContractPlanId); const { data: inLeaseItems, loading: inLeaseItemsLoading } = useLeaseItems(inContractPlanId);
const { data: inFeeseItems, loading: inFeeItemsLoading } = useProductFeeItems(inContractPlanId);
const { data: outLeaseItems, loading: outLeaseItemsLoading } = useLeaseItems(outContractPlanId); const { data: outLeaseItems, loading: outLeaseItemsLoading } = useLeaseItems(outContractPlanId);
const { data: outFeeseItems, loading: outFeeItemsLoading } = useProductFeeItems(outContractPlanId);
const contractProducts = /**
leaseItems?.data.reduce((acc, current) => { * trans
acc.push(...current.products.map((item) => ({ id: item.category_id }))); */
return acc; const transFee = (fee: any[]) => {
}, []) ?? []; if (fee) {
const feeData = fee.map((fee) => {
const inContractProducts = const data = {
inLeaseItems?.data.reduce((acc, current) => { ...fee,
acc.push(...current.products.map((item) => ({ id: item.category_id }))); };
return acc; data.products = [data.fee_product];
}, []) ?? []; return data;
});
const outContractProducts = return feeData || [];
outLeaseItems?.data.reduce((acc, current) => { } else {
acc.push(...current.products.map((item) => ({ id: item.category_id }))); 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 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 = const priceProducts =
form.values.price_items?.filter((item) => item.product).map((item) => ({ id: item.product.category_id })) ?? []; 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) { switch (form.values.category) {
case RecordCategory.lease: case RecordCategory.lease:
@ -53,10 +92,10 @@ export const RecordProductScope = observer(() => {
case RecordCategory.purchase: case RecordCategory.purchase:
required = { price: true, contract: false, inContract: false, outContract: false }; required = { price: true, contract: false, inContract: false, outContract: false };
break; break;
case RecordCategory.lease2lease: // 出库合同,入库合同的产品范围交集 case RecordCategory.lease2lease:
required = { price: false, contract: false, inContract: true, outContract: true }; required = { price: false, contract: false, inContract: true, outContract: true };
break; break;
case RecordCategory.purchase2lease: // 报价,入库合同的产品范围交集 case RecordCategory.purchase2lease: // 报价,入库合同的产品范围交集 加入入库合同的费用数据
required = { price: true, contract: false, inContract: true, outContract: false }; required = { price: true, contract: false, inContract: true, outContract: false };
break; break;
case RecordCategory.inventory: case RecordCategory.inventory:
@ -69,24 +108,56 @@ export const RecordProductScope = observer(() => {
let result = data?.data ?? []; let result = data?.data ?? [];
if (required.inContract && required.outContract) { if (required.inContract && required.outContract) {
// 租赁直发单 // 租赁直发单
const intersection = _.intersectionBy(inContractProducts, outContractProducts, 'id'); const intersection = [
result = _.intersectionBy(result, intersection, 'id'); _.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) { } else if (required.price && required.inContract) {
const intersection = _.intersectionBy(inContractProducts, priceProducts, 'id'); const intersection = [
result = _.intersectionBy(result, intersection, 'id'); _.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) { } else if (required.price) {
result = _.intersectionBy(result, priceProducts, 'id'); const data = intersectionByMultiple(result, priceProducts, 'category_id');
result = [_.intersectionBy(result, priceProducts, 'id'), data].flat();
} else if (required.contract) { } else if (required.contract) {
result = _.intersectionBy(result, contractProducts, 'id'); const data = intersectionByMultiple(result, contractProducts, 'category_id');
result = [_.intersectionBy(result, contractProducts, 'id'), data].flat();
} }
useDeepCompareEffect(() => { useDeepCompareEffect(() => {
form.setValues({ form.setValues({
product_scope: result, product_scope: result,
}); });
}, [result, form]); }, [result, form]);
return loading || leaseItemsLoading || inLeaseItemsLoading || outLeaseItemsLoading ? <Spin /> : <></>; return loading ||
leaseItemsLoading ||
inLeaseItemsLoading ||
outLeaseItemsLoading ||
feeItemsLoading ||
inFeeItemsLoading ||
outFeeItemsLoading ? (
<Spin />
) : (
<></>
);
}) as CustomFC; }) as CustomFC;
RecordProductScope.displayName = 'RecordProductScope'; RecordProductScope.displayName = 'RecordProductScope';
RecordProductScope.__componentType = CustomComponentType.CUSTOM_ASSOCIATED_FIELD; RecordProductScope.__componentType = CustomComponentType.CUSTOM_ASSOCIATED_FIELD;
RecordProductScope.__componentLabel = '记录单 - 产品范围'; 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

@ -30,6 +30,30 @@ export const useLeaseItems = (planId) => {
return { data, loading }; return { data, loading };
}; };
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) => { export const useFeeItems = (categoryId, planId) => {
const { data, loading, run } = useCachedRequest<any>( const { data, loading, run } = useCachedRequest<any>(
{ {

View File

@ -133,6 +133,7 @@ const styles = StyleSheet.create({
marginLeft: '-1px', marginLeft: '-1px',
borderTop: '1px solid black', borderTop: '1px solid black',
marginTop: '-1px', marginTop: '-1px',
borderBottom: '1px solid black',
}, },
tableCellLast: { tableCellLast: {
flex: 1, flex: 1,
@ -302,6 +303,81 @@ const PreviewDocument = ({
dobuleRecord.push(result); dobuleRecord.push(result);
} }
} }
const renderRecord = (data, isDouble) => {
const columns = isDouble ? ['left_', 'right_'] : [''];
const page = data.map((item, index) => (
<View key={index} style={styles.tableContent}>
{columns.map((column, columnIndex) => (
<React.Fragment key={columnIndex}>
<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={styles.tableCell}>
{!item[column + 'total']
? ''
: formatQuantity(item[column + 'total'], 2) + item[column + 'conversion_unit']}
</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.category === RecordCategory.purchase) {
const allprice = getAllPrice();
addCol = (
<View style={styles.tableContent}>
{columns.map((column, columnIndex) => (
<React.Fragment key={columnIndex}>
<Text style={styles.tableCell2}></Text>
<Text style={styles.tableCell}>{column === 'left_' ? '' : '总金额'}</Text>
<Text style={styles.tableCell}>{column === 'left_' ? '' : allprice}</Text>
<Text style={!isDouble || column === 'right_' ? styles.tableCellLast : styles.tableCell}></Text>
</React.Fragment>
))}
</View>
);
}
return detail.category === RecordCategory.purchase ? (
<>
{page}
{addCol}
</>
) : (
page
);
};
const renderPrice = (data, isDouble) => {
const columns = isDouble ? ['left_', 'right_'] : [''];
return data.map((item, index) => (
<View key={index} style={styles.tableContent}>
{columns.map((column, columnIndex) => (
<React.Fragment key={columnIndex}>
<Text style={styles.tableCell2}>{item[column + 'name']}</Text>
<Text style={styles.tableCell}>
{formatQuantity(item[column + 'unit_price'], 2) + '元/' + item[column + 'unit']}
</Text>
<Text style={styles.tableCell}>
{formatQuantity(item[column + 'count'], 2)}
{item[column + 'unit']}
</Text>
<Text style={styles.tableCell}>{formatCurrency(item[column + 'all_price'], 2)}</Text>
<Text style={!isDouble || column === 'right_' ? styles.tableCellLast : styles.tableCell}>
{item[column + 'comment'] || ''}
</Text>
</React.Fragment>
))}
</View>
));
};
return ( return (
<Document> <Document>
<Page size="A4" style={{ ...styles.page, marginTop: detail.margingTop }}> <Page size="A4" style={{ ...styles.page, marginTop: detail.margingTop }}>
@ -365,216 +441,44 @@ const PreviewDocument = ({
<Text style={styles.headerLeftRight}>{car}</Text> <Text style={styles.headerLeftRight}>{car}</Text>
</View> </View>
)} )}
{/* 单列定价 */} {/* 定价 */}
{!isDouble && detail.category === RecordCategory.purchase && ( {detail.category === RecordCategory.purchase && (
<View style={styles.tableContentTitle}> <View style={styles.tableContentTitle}>
<Text style={styles.tableCellLargeTitle}></Text> <Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitleLast}></Text> <Text style={!isDouble ? styles.tableCellTitleLast : styles.tableCellTitle}></Text>
</View> {isDouble && (
)} <>
{!isDouble &&
detail.category === RecordCategory.purchase &&
priceRule.length &&
priceRule.map((item) => (
<View key={'key'} style={styles.tableContent}>
<Text style={styles.tableCell2}>{item.name}</Text>
<Text style={styles.tableCell}>
{item?.unit_price && formatQuantity(item.unit_price, 2) + '元/' + item.unit}
</Text>
<Text style={styles.tableCell}>
{formatQuantity(item.count, 2)}
{item.unit}
</Text>
<Text style={styles.tableCell}>{formatCurrency(item.all_price, 2)}</Text>
<Text style={styles.tableCellLast}>{item.comment || ''}</Text>
</View>
))}
{/* 双列定价 */}
{isDouble && detail.category === RecordCategory.purchase && (
<View style={styles.tableContentTitle}>
<Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellLargeTitle}></Text> <Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitleLast}></Text> <Text style={styles.tableCellTitleLast}></Text>
</>
)}
</View> </View>
)} )}
{isDouble && {detail.category === RecordCategory.purchase &&
detail.category === RecordCategory.purchase && renderPrice(isDouble ? dobulePriceRule : priceRule, isDouble)}
priceRule.length &&
dobulePriceRule.map((item) => (
<View key={'key'} style={styles.tableContent}>
<Text style={styles.tableCell2}>{item.left_name}</Text>
<Text style={styles.tableCell}>
{item?.left_unit_price && formatQuantity(item.left_unit_price, 2) + '元/' + item.left_unit}
</Text>
<Text style={styles.tableCell}>
{formatQuantity(item.left_count, 2)}
{item.left_unit}
</Text>
<Text style={styles.tableCell}>{formatCurrency(item.left_all_price, 2)}</Text>
<Text style={styles.tableCell}>{item.left_comment || ''}</Text>
<Text style={styles.tableCell2}>{item.right_name}</Text>
<Text style={styles.tableCell}>
{item?.right_unit_price && formatQuantity(item.right_unit_price, 2) + '元/' + item.right_unit}
</Text>
<Text style={styles.tableCell}>
{item.right_count && formatQuantity(item.right_count)}
{item.right_unit}
</Text>
<Text style={styles.tableCell}>
{item.right_all_price && formatCurrency(item.right_all_price, 2)}
</Text>
<Text style={styles.tableCellLast}>{item.right_comment || ''}</Text>
</View>
))}
{/* ============================================================两表分割================================================================================= */} {/* ============================================================两表分割================================================================================= */}
{/* 单列租金及费用 */} {/* 单列租金及费用 */}
{!isDouble && (
<View style={styles.tableContentTitle}> <View style={styles.tableContentTitle}>
<Text style={styles.tableCellLargeTitle}></Text> <Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text <Text style={!isDouble ? styles.tableCellTitleLast : styles.tableCellTitle}></Text>
style={
detail.category === RecordCategory.purchase ? styles.tableCell2TitleLast : styles.tableCellTitleLast
}
>
</Text>
</View>
)}
{!isDouble &&
record.map((item) => (
<View key={'key'} style={styles.tableContent}>
<Text style={styles.tableCell2}>{item.name}</Text>
<Text style={styles.tableCell}>
{!item.count || item.isFee ? '' : formatQuantity(item.count, 2) + item.unit}
</Text>
<Text style={styles.tableCell}>
{(printSetup === PrintSetup.Display && !item.product_id && !item.isTotal) ||
(printSetup === PrintSetup.Manual && !item.product_id && !item.isTotal)
? ''
: item.total
? formatQuantity(item.total, 2)
: ''}
{item.isFee && !item.product_id // 判断未无产品关联费用
? printSetup === PrintSetup.DisplayAndPrice && item.total
? '元'
: ''
: item.conversion_unit}
</Text>
<Text
style={detail.category === RecordCategory.purchase ? styles.tableCell2Last : styles.tableCellLast}
>
{item.isExcluded ? '不计入合同 ' + (item.comment || '') : item.comment || ''}
</Text>
</View>
))}
{!isDouble && detail.category === RecordCategory.purchase && (
<View key={'key'} style={styles.tableContent}>
<Text style={styles.tableCell2}></Text>
<Text style={styles.tableCell}></Text>
<Text style={styles.tableCell}>{getAllPrice()}</Text>
<Text
style={detail.category === RecordCategory.purchase ? styles.tableCell2Last : styles.tableCellLast}
></Text>
</View>
)}
{/* 双列租及费用 */}
{isDouble && ( {isDouble && (
<View style={styles.tableContentTitle}> <>
<Text style={styles.tableCellLargeTitle}></Text> <Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text> <Text style={styles.tableCellTitle}></Text>
<Text <Text style={styles.tableCellTitleLast}></Text>
style={detail.category === RecordCategory.purchase ? styles.tableCell2Title : styles.tableCellTitle} </>
>
</Text>
<Text style={styles.tableCellLargeTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
<Text style={styles.tableCellTitle}></Text>
<Text
style={
detail.category === RecordCategory.purchase ? styles.tableCell2TitleLast : styles.tableCellTitleLast
}
>
</Text>
</View>
)} )}
{isDouble &&
dobuleRecord.map((item) => (
<View key={'key'} style={styles.tableContent}>
<Text style={styles.tableCell2}>{item.left_name}</Text>
<Text style={styles.tableCell}>
{!item.left_count || item.left_isFee ? '' : formatQuantity(item.left_count, 2) + item.left_unit}
</Text>
<Text style={styles.tableCell}>
{(printSetup === PrintSetup.Display && !item.left_product_id && !item.left_isTotal) ||
(printSetup === PrintSetup.Manual && !item.left_product_id && !item.left_isTotal)
? ''
: item.left_total
? formatQuantity(item.left_total, 2)
: ''}
{item.left_isFee && !item.left_product_id // 判断未无产品关联费用
? printSetup === PrintSetup.DisplayAndPrice && item.left_total
? '元'
: '' // 不记录合同不需要单位
: item.left_conversion_unit}
</Text>
<Text style={detail.category === RecordCategory.purchase ? styles.tableCell2 : styles.tableCell}>
{item.left_isExcluded ? '不计入合同 ' + (item.left_comment || '') : item.left_comment || ''}
</Text>
<Text style={styles.tableCell2}>{item.right_name}</Text>
<Text style={styles.tableCell}>
{!item.right_count || item.right_isFee ? '' : formatQuantity(item.right_count, 2) + item.right_unit}
</Text>
<Text style={styles.tableCell}>
{(printSetup === PrintSetup.Display && !item.right_product_id && !item.right_isTotal) ||
(printSetup === PrintSetup.Manual && !item.right_product_id && !item.right_isTotal)
? ''
: item.right_total
? formatQuantity(item.right_total, 2)
: ''}
{item.right_isFee && !item.right_product_id // 判断未无产品关联费用
? printSetup === PrintSetup.DisplayAndPrice && item.right_total
? '元'
: '' // 不记录合同不需要单位
: item.right_conversion_unit}
</Text>
<Text
style={detail.category === RecordCategory.purchase ? styles.tableCell2Last : styles.tableCellLast}
>
{item.right_name && item.right_isExcluded
? '不计入合同 ' + (item.right_comment || '')
: item.right_comment || ''}
</Text>
</View> </View>
))} {renderRecord(isDouble ? dobuleRecord : record, isDouble)}
{isDouble && detail.category === RecordCategory.purchase && (
<View key={'key'} style={styles.tableContent}>
<Text style={styles.tableCell2}></Text>
<Text style={styles.tableCell}></Text>
<Text style={styles.tableCell}></Text>
<Text style={detail.category === RecordCategory.purchase ? styles.tableCell2 : styles.tableCell}></Text>
<Text style={styles.tableCell2}></Text>
<Text style={styles.tableCell}></Text>
<Text style={styles.tableCell}>{getAllPrice()}</Text>
<Text
style={detail.category === RecordCategory.purchase ? styles.tableCell2Last : styles.tableCellLast}
></Text>
</View>
)}
<View style={styles.tableFooter}> <View style={styles.tableFooter}>
<View style={styles.tableFooterQr}> <View style={styles.tableFooterQr}>
<Image src={imageUrl} /> <Image src={imageUrl} />

View File

@ -1,11 +1,14 @@
import { ConversionLogics, RecordCategory, RulesNumber, SourcesType, countCource } from '../../utils/constants'; import { ConversionLogics, RecordCategory, RulesNumber, SourcesType, countCource } from '../../utils/constants';
import { renderItV2 } from '../pdf-documents/records-documentV2'; import { renderItV2 } from '../pdf-documents/records-documentV2';
import { Service } from '@nocobase/utils'; import { Db, Service } from '@nocobase/utils';
import { RecordPdfOptions } from '../../interfaces/options'; import { RecordPdfOptions } from '../../interfaces/options';
import { PrintSetup } from '../../utils/system'; import { PrintSetup } from '../../utils/system';
import Database from '@nocobase/database';
@Service() @Service()
export class RecordPdfService { export class RecordPdfService {
@Db()
private db: Database;
async transformPdfV2(recordData: any, lease_data: any, fee_data: any, options: RecordPdfOptions) { async transformPdfV2(recordData: any, lease_data: any, fee_data: any, options: RecordPdfOptions) {
const { isDouble, printSetup } = options; const { isDouble, printSetup } = options;
@ -79,7 +82,7 @@ export class RecordPdfService {
make_price = setData; make_price = setData;
} }
// 计算租金数据 // !产品
const leaseData = lease_data const leaseData = lease_data
.map((lease) => { .map((lease) => {
const data = { const data = {
@ -117,8 +120,7 @@ export class RecordPdfService {
}) })
.sort((a, b) => a.category_id - b.category_id); .sort((a, b) => a.category_id - b.category_id);
// 计算费用数据,包括有产品关联跟无产品关联 // !产品赔偿
// 关联产品的赔偿(人工录入/全部)
const product_fee = const product_fee =
printSetup === PrintSetup.Manual printSetup === PrintSetup.Manual
? fee_data.filter((item) => item.product_id && item.count_source === SourcesType.staff) ? fee_data.filter((item) => item.product_id && item.count_source === SourcesType.staff)
@ -148,68 +150,109 @@ export class RecordPdfService {
category_id: item.product_category_id, category_id: item.product_category_id,
}; };
if (item.conversion_logic_id === ConversionLogics.Keep) { if (item.conversion_logic_id === ConversionLogics.Keep) {
data['count'] = data.count1; data['total'] = data.count1;
} else if (item.conversion_logic_id === ConversionLogics.Product) { } else if (item.conversion_logic_id === ConversionLogics.Product) {
if (item.convertible) data['count'] = data.count1 * item.product_ratio; if (item.convertible) data['total'] = data.count1 * item.product_ratio;
else data['count'] = data.count1; else data['total'] = data.count1;
} else if (item.conversion_logic_id === ConversionLogics.ProductWeight) { } else if (item.conversion_logic_id === ConversionLogics.ProductWeight) {
data['count'] = data.count1 * item.product_weight; data['total'] = data.count1 * item.product_weight;
} else if (item.conversion_logic_id === ConversionLogics.ActualWeight) { } else if (item.conversion_logic_id === ConversionLogics.ActualWeight) {
data['count'] = item.actual_weight || item.record_weight; data['total'] = item.actual_weight || item.record_weight;
} else { } else {
if (item.weight_rules?.conversion_logic_id === ConversionLogics.Keep) { if (item.weight_rules?.conversion_logic_id === ConversionLogics.Keep) {
data['count'] = data.count1 * item.weight_rules.weight; data['total'] = data.count1 * item.weight_rules.weight;
} else if (item.weight_rules?.conversion_logic_id === ConversionLogics.Product) { } else if (item.weight_rules?.conversion_logic_id === ConversionLogics.Product) {
data['count'] = data.count1 * item.weight_rules.weight * item.product_ratio; data['total'] = data.count1 * item.weight_rules.weight * item.product_ratio;
} }
} }
// 如果 printSetup === PrintSetup.DisplayAndPrice 要计算费用产生的价格 data['conversion_unit'] = item.unit;
if (printSetup === PrintSetup.DisplayAndPrice) { if (printSetup === PrintSetup.DisplayAndPrice) {
data['total'] = data['count'] * item.unit_price; data['count'] = data['total'];
data['total'] = data['total'] * item.unit_price;
data['conversion_unit'] = '元'; data['conversion_unit'] = '元';
} }
return data; return data;
}); });
// 无关联产品赔偿, 人工录取的情况直接为空因为excludedFee已经处理正常情况取非手动录入情况数据excludedFee已经处理 // !无关联赔偿 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 = const fee =
printSetup === PrintSetup.Manual printSetup === PrintSetup.Manual
? [] ? []
: fee_data.filter((item) => !item.product_id && item.count_source !== SourcesType.staff); : fee_data.filter((item) => !item.product_id && item.count_source !== SourcesType.staff);
const no_product_fee = fee const no_product_fee = fee
.map((item) => { .map((item) => {
// 购销/暂存/盘点无费用信息
if ( if (
recordData.category === RecordCategory.purchase || recordData.category === RecordCategory.purchase ||
recordData.category === RecordCategory.staging || recordData.category === RecordCategory.staging ||
recordData.category === RecordCategory.inventory recordData.category === RecordCategory.inventory
) ) {
return; return;
if (item.count_source === countCource.artificial) {
const data = recordData.record_fee_items?.find((hava) => hava.product?.id === item.fee_product_id);
if (!data) return;
} }
if (item.record_movement === '-1' && item.count_source === SourcesType.inbound) return; if (item.record_movement === '-1' && item.count_source === SourcesType.inbound) return;
if (item.record_movement === '1' && item.count_source === SourcesType.outbound) return; if (item.record_movement === '1' && item.count_source === SourcesType.outbound) return;
const data = { const data = {
isFee: true, isFee: true,
name: item.custom_name || item.label, name: item.custom_name || item.label,
unit: '', unit: item.unit,
conversion_unit: item.unit,
comment: '', comment: '',
}; };
if (item.conversion_logic_id === ConversionLogics.Keep) { if (item.conversion_logic_id === ConversionLogics.Keep) {
data['count'] = lease_data.reduce((a, b) => a + b.count, 0); data['total'] = lease_data.reduce((a, b) => a + b.count, 0);
} else if (item.conversion_logic_id === ConversionLogics.Product) { } else if (item.conversion_logic_id === ConversionLogics.Product) {
data['count'] = lease_data.reduce((a, b) => a + (item.convertible ? b.count * b.ratio : b.count), 0); 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) { } else if (item.conversion_logic_id === ConversionLogics.ProductWeight) {
data['count'] = lease_data.reduce((a, b) => a + b.count * b.weight, 0) / 1000; data['total'] = lease_data.reduce((a, b) => a + b.count * b.weight, 0) / 1000;
} else if (item.conversion_logic_id === ConversionLogics.ActualWeight) { } else if (item.conversion_logic_id === ConversionLogics.ActualWeight) {
data['count'] = item.record_weight; data['total'] = item.record_weight;
} else { } else {
if (item.weight_rules?.length) { if (item.weight_rules?.length) {
// 计算有误,需要检查 data['total'] = lease_data.reduce((a, b) => {
data['count'] = lease_data.reduce((a, b) => {
const weight_rule = item.weight_rules.find( const weight_rule = item.weight_rules.find(
(l) => l.product_id === b.product_id || l.product - RulesNumber === b.category_id, (l) => l.product_id === b.product_id || l.product - RulesNumber === b.category_id,
); );
@ -221,34 +264,15 @@ export class RecordPdfService {
}, 0); }, 0);
} }
} }
// 如果 printSetup === PrintSetup.DisplayAndPrice 要计算费用产生的价格 data['conversion_unit'] = '吨';
if (printSetup === PrintSetup.DisplayAndPrice) { if (printSetup === PrintSetup.DisplayAndPrice) {
data['total'] = data['count'] * item.unit_price; data['count'] = data['total'];
data['total'] = data['total'] * item.unit_price;
data['conversion_unit'] = '元';
} }
return data; return data;
}) })
.filter((item) => item && item.count); .filter((item) => item && item.total);
// 注意把手动录入的加入
const manualData = recordData.record_fee_items;
// 因为以往逻辑是根据规则查询,这个订单中数据智能单独查询
const excludedFee =
manualData
?.map((item) => {
const data = {
isFee: true,
name: item.product.custom_name || item.product.label,
comment: '',
count: item.count,
isExcluded: item.is_excluded,
};
const feeRule = fee_data.find((fee) => fee.fee_product_id === item.product_id);
if (feeRule) {
data['total'] = feeRule.unit_price * item.count;
data['unit'] = '';
}
return data;
})
.filter(Boolean) || [];
const product_fees = transFormProductFee.filter(Boolean); const product_fees = transFormProductFee.filter(Boolean);
// 内容(租金+费用) // 内容(租金+费用)
@ -280,10 +304,10 @@ export class RecordPdfService {
product_correlation.push(itemB); product_correlation.push(itemB);
} }
}); });
const recordPdfData = [...product_correlation, ...no_product_fee, ...excludedFee]; const recordPdfData = [...product_correlation, ...manualData, ...no_product_fee].filter(Boolean);
return await renderItV2({ return await renderItV2({
detail: recordData, detail: recordData,
record: recordPdfData, // 租金+费用,无关联费用 record: recordPdfData,
priceRule: make_price, priceRule: make_price,
isDouble, isDouble,
printSetup, printSetup,

View File

@ -99,19 +99,6 @@ SELECT
WHERE WHERE
r.id = rv.record_id r.id = rv.record_id
) AS vehicles, ) AS vehicles,
(
SELECT
JSONB_AGG(
TO_JSONB(
JSONB_SET(TO_JSONB(rfi), '{product}', TO_JSONB(p))
)
)
FROM
record_fee_items rfi
JOIN product p ON p.id = rfi.product_id
WHERE
rfi.record_id = r.id
) AS record_fee_items,
-- 查询分组实际重量 -- 查询分组实际重量
( (
SELECT SELECT