diff --git a/packages/plugins/@hera/plugin-rental/package.json b/packages/plugins/@hera/plugin-rental/package.json index 88bb313b6..1e7ce8a8a 100644 --- a/packages/plugins/@hera/plugin-rental/package.json +++ b/packages/plugins/@hera/plugin-rental/package.json @@ -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" diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordDetails.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordDetails.tsx index 4c8568bc2..9d64f38a9 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordDetails.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordDetails.tsx @@ -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({ 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({ - 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(
); } + 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 ; } diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordFeeConverted.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordFeeConverted.tsx index 7bb6b2a73..7af13b339 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordFeeConverted.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordFeeConverted.tsx @@ -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({ - resource: 'weight_rules', +export const RecordFeeConvertedAmount: CustomFunctionComponent = () => { + // 查数据,查里面的费用关联的数据 + const contractPlans = useRequest({ + 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({ + 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 {`${result['count'] || 0}${result['unit'] || ''}`}; -}) 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 {result}; +}; 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; -}; diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordFeeScope.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordFeeScope.tsx index 608a7f4cb..34f7577d5 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordFeeScope.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordFeeScope.tsx @@ -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 ? : <>; + form.setValuesIn(fieldPath, result); + }, [result, form, fieldPath]); + + return ((form.values.record_category === '1' || form.values.record_category === '0') && contractLoading) || + _loading ? ( + + ) : ( + <> + ); }) 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; -}; diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemCount.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemCount.tsx index a78920bbb..8b8759f61 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemCount.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemCount.tsx @@ -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(param); + + if (!data && loading) { return ; } 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 {formatQuantity(value, 2) + unit}; } return - ; diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemValuationQuantity.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemValuationQuantity.tsx index c09d9c64c..14cade392 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemValuationQuantity.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemValuationQuantity.tsx @@ -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({ + resource: 'product', + action: 'list', + params: { + appends: ['category'], + pageSize: 99999, + }, + }); const reqWeightRules = useCachedRequest({ 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; - } -}; diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemWeight.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemWeight.tsx index f1842ff5c..f73457f5d 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemWeight.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordItemWeight.tsx @@ -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 {formatQuantity(value, 3) + '吨'}; + return {formatQuantity(value, 2) + '吨'}; } } return - ; diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordProductScope.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordProductScope.tsx index c8404002b..50444e49e 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordProductScope.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordProductScope.tsx @@ -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({ + 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 ? : <>; + return loading || + leaseItemsLoading || + inLeaseItemsLoading || + outLeaseItemsLoading || + feeItemsLoading || + inFeeItemsLoading || + outFeeItemsLoading ? ( + + ) : ( + <> + ); }) 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; +}; diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordSummary.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordSummary.tsx index 292250473..fc65ccb46 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordSummary.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordSummary.tsx @@ -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({ resource: 'weight_rules', action: 'list', @@ -24,75 +23,136 @@ export const RecordSummary = observer((): any => { pageSize: 99999, }, }); - const { data: products } = useProducts(); + const reqProduct = useCachedRequest({ + 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: , + }; + return data; } - const data = { - label: item['label'], - key: item['label'], - children: , - }; - 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); }; diff --git a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordTotalPrice.tsx b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordTotalPrice.tsx index 02c839282..b52adc623 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordTotalPrice.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/custom-components/RecordTotalPrice.tsx @@ -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; diff --git a/packages/plugins/@hera/plugin-rental/src/client/hooks/index.tsx b/packages/plugins/@hera/plugin-rental/src/client/hooks/index.tsx index e972dfd7a..ab02167dd 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/hooks/index.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/hooks/index.tsx @@ -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

(params: {}, options = {}) { const cacheKey = stringify(params); return useRequest

(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({ - 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(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( - { - 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(feeParams, { + manual: true, + }); + useEffect(() => { + if (planId) { + run(); + } + }, [planId]); + return { data, loading }; +}; + +export const useFeeItems = (categoryId, planId) => { + const { data, loading, run } = useCachedRequest( + { + 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 }; }; diff --git a/packages/plugins/@hera/plugin-rental/src/client/hooks/usePdfPath.tsx b/packages/plugins/@hera/plugin-rental/src/client/hooks/usePdfPath.tsx index 734f5f016..a66b6f2cd 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/hooks/usePdfPath.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/hooks/usePdfPath.tsx @@ -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 ( - - - - - - {props.children} - - - - - + + + {props.children} + + ); }; 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; }; diff --git a/packages/plugins/@hera/plugin-rental/src/client/index.tsx b/packages/plugins/@hera/plugin-rental/src/client/index.tsx index f41d112c9..5829e0728 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/index.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/index.tsx @@ -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, diff --git a/packages/plugins/@hera/plugin-rental/src/client/schema-initializer/actions/RecordPrintSetupActionInitializer.tsx b/packages/plugins/@hera/plugin-rental/src/client/schema-initializer/actions/RecordPrintSetupActionInitializer.tsx index 31052bcb2..bebc8d6da 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/schema-initializer/actions/RecordPrintSetupActionInitializer.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/schema-initializer/actions/RecordPrintSetupActionInitializer.tsx @@ -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({ diff --git a/packages/plugins/@hera/plugin-rental/src/client/schema-initializer/actions/SettlementExportExcel.tsx b/packages/plugins/@hera/plugin-rental/src/client/schema-initializer/actions/SettlementExportExcel.tsx index b619411da..f3d17a613 100644 --- a/packages/plugins/@hera/plugin-rental/src/client/schema-initializer/actions/SettlementExportExcel.tsx +++ b/packages/plugins/@hera/plugin-rental/src/client/schema-initializer/actions/SettlementExportExcel.tsx @@ -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(); diff --git a/packages/plugins/@hera/plugin-rental/src/interfaces/options.ts b/packages/plugins/@hera/plugin-rental/src/interfaces/options.ts index f94108ea2..cf752c863 100644 --- a/packages/plugins/@hera/plugin-rental/src/interfaces/options.ts +++ b/packages/plugins/@hera/plugin-rental/src/interfaces/options.ts @@ -1,6 +1,4 @@ export interface RecordPdfOptions { isDouble: number; printSetup: String; - margingTop: number; - paper: string; } diff --git a/packages/plugins/@hera/plugin-rental/src/interfaces/waybill.ts b/packages/plugins/@hera/plugin-rental/src/interfaces/waybill.ts index 1c9a07f67..ad2196a29 100644 --- a/packages/plugins/@hera/plugin-rental/src/interfaces/waybill.ts +++ b/packages/plugins/@hera/plugin-rental/src/interfaces/waybill.ts @@ -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; } diff --git a/packages/plugins/@hera/plugin-rental/src/server/actions/records-controller.ts b/packages/plugins/@hera/plugin-rental/src/server/actions/records-controller.ts index 7493da2dd..a77fe7eb5 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/actions/records-controller.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/actions/records-controller.ts @@ -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); diff --git a/packages/plugins/@hera/plugin-rental/src/server/actions/settlement-controller.ts b/packages/plugins/@hera/plugin-rental/src/server/actions/settlement-controller.ts index 142493776..d7103ea93 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/actions/settlement-controller.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/actions/settlement-controller.ts @@ -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') diff --git a/packages/plugins/@hera/plugin-rental/src/server/actions/waybills-controller.ts b/packages/plugins/@hera/plugin-rental/src/server/actions/waybills-controller.ts index 9fbc0e508..860927d05 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/actions/waybills-controller.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/actions/waybills-controller.ts @@ -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') diff --git a/packages/plugins/@hera/plugin-rental/src/server/migrations/20240122202859-update-record_category_script.ts b/packages/plugins/@hera/plugin-rental/src/server/migrations/20240122202859-update-record_category_script.ts index 52fca3171..8e298e88c 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/migrations/20240122202859-update-record_category_script.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/migrations/20240122202859-update-record_category_script.ts @@ -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' diff --git a/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/records-documentV2.tsx b/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/records-documentV2.tsx index 678561c44..3d6b9585c 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/records-documentV2.tsx +++ b/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/records-documentV2.tsx @@ -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 0为单列,1为双列 @@ -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 = ({ {columns.map((column, columnIndex) => ( - - {annotate && item[column + 'commentIndex'] - ? item[column + 'commentIndex'] + '……' + item[column + 'name'] - : item[column + 'name']} - + {item[column + 'name']} {!item[column + 'count'] ? '' : formatQuantity(item[column + 'count'], 2) + item[column + 'unit']} - + {!item[column + 'total'] ? '' : formatQuantity(item[column + 'total'], 2) + item[column + 'conversion_unit']} - {!annotate && ( - - {item[column + 'isExcluded'] - ? '不计入合同 ' + (item[column + 'comment'] || '') - : item[column + 'comment'] || ''} - - )} + + {item[column + 'isExcluded'] + ? '不计入合同 ' + (item[column + 'comment'] || '') + : item[column + 'comment'] || ''} + ))} )); let addCol = <>; - if (detail.record_category === '购销') { + if (detail.category === RecordCategory.purchase) { const allprice = getAllPrice(); addCol = ( @@ -319,27 +336,15 @@ const PreviewDocument = ({ {column === 'left_' ? '' : '总金额'} - - {column === 'left_' ? '' : allprice} - - {!annotate && ( - - )} + {column === 'left_' ? '' : allprice} + ))} ); } - 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 ( - - {' '} - {/**transform: `scale(${trans})` */} - - {!detail.record_category - ? detail.plate - : detail.contract_first_party?.name || detail.systemName || '异常数据,请联系相关负责人!'} - - {detail.contract_first_party ? detail.movement : '盘点'}单 + + {recordsName} + {detail.category === RecordCategory.inventory ? '盘点' : outOfStorage}单 - {detail.record_category === '租赁' && ( - - - 承租单位:{detail.record_party_b.company.name || ''} - - 日期:{detail.record_date && dayjs(detail.record_date).format('YYYY-MM-DD')} - - - - - 项目名称:{detail.record_party_b.name || ''} - 流水号:{detail.record_number} - - - - 项目地址:{detail.record_party_b.address} - 原始单号:{detail.record_origin} - - - - 项目联系人:{projectPhone} - 车号:{car} - + {/* 第一行 */} + + {detail.category === RecordCategory.purchase && ( + 销售单位:{outOfStorageAddress(1)} + )} + {detail.category === RecordCategory.staging && ( + 出库方:{outOfStorageAddress(1)} + )} + {detail.category === RecordCategory.inventory && ( + 盘点单位:{detail.in_stock?.name} + )} + {detail.category !== RecordCategory.lease && ( + 日期:{date && dayjs(date).format('YYYY-MM-DD')} + )} + {detail.category !== RecordCategory.lease && 流水号:{number}} + + {/* 第二行 */} + + {detail.category === RecordCategory.purchase && ( + 采购单位:{outOfStorageAddress(0)} + )} + {detail.category === RecordCategory.staging && ( + 入库方:{outOfStorageAddress(0)} + )} + {detail.category !== RecordCategory.inventory && detail.category !== RecordCategory.lease && ( + 车号:{car} + )} + {detail.category !== RecordCategory.inventory && detail.category !== RecordCategory.lease && ( + 原始单号:{origin} + )} + + {/* 租赁 */} + {detail.category === RecordCategory.lease && ( + + 承租单位:{detail.contract?.project?.company?.name || ''} + 日期:{date && dayjs(date).format('YYYY-MM-DD')} )} - - {(detail.record_category === '购销' || detail.record_category === '暂存') && ( - - - - {detail.record_category === '暂存' ? '出库方:' : '销售单位:'} - {detail.record_party_b?.company?.name || ''} {detail.record_party_b.name || ''} - - - 日期:{detail.record_date && dayjs(detail.record_date).format('YYYY-MM-DD')} - - 流水号:{detail.record_number} - - - - {detail.record_category === '暂存' ? '入库方:' : '采购单位:'} - {detail.record_party_a.name || ''} - - 车号:{car} - 原始单号:{detail.record_origin} - + {detail.category === RecordCategory.lease && ( + + 项目名称:{detail.contract?.project?.name || ''} + 流水号:{number} )} - {!detail.record_category && ( - - - 盘点单位:{detail.plate || ''} - - 日期:{detail.record_date && dayjs(detail.record_date).format('YYYY-MM-DD')} - - 流水号:{detail.record_number} - + {detail.category === RecordCategory.lease && ( + + 项目地址:{detail.contract?.project?.address} + 原始单号:{origin} + + )} + {detail.category === RecordCategory.lease && ( + + 项目联系人:{projectPhone} + 车号:{car} )} - {/* 定价 */} - {detail.record_category === '购销' && ( + {detail.category === RecordCategory.purchase && ( 物料名称及规格 单价 @@ -470,20 +463,21 @@ const PreviewDocument = ({ )} )} - {detail.record_category === '购销' && renderPrice(isDouble ? dobulePriceRule : priceRule, isDouble)} + {detail.category === RecordCategory.purchase && + renderPrice(isDouble ? dobulePriceRule : priceRule, isDouble)} {/* ============================================================两表分割================================================================================= */} {/* 单列租金及费用 */} 物料名称及规格 数量 小计 - {!annotate && 备注} + 备注 {isDouble && ( <> 物料名称及规格 数量 - 小计 - {!annotate && 备注} + 小计 + 备注 )} @@ -501,21 +495,14 @@ const PreviewDocument = ({ - 制表人:{detail.nickname + ' ' + detail?.userPhone} - {detail.record_category === '租赁' && 出租单位(签名):} - {detail.record_category === '租赁' && 租借单位(签名):} - {detail.record_category === '购销' && 采购单位(签名):} - {detail.record_category === '购销' && 购入单位(签名):} - {!detail.record_category && 盘点仓库(签名):} + 制表人:{singlePlayer} + {detail.category === RecordCategory.lease && 出租单位(签名):} + {detail.category === RecordCategory.lease && 租借单位(签名):} + {detail.category === RecordCategory.purchase && 采购单位(签名):} + {detail.category === RecordCategory.purchase && 购入单位(签名):} + {detail.category === RecordCategory.staging && 暂存仓库(签名):} + {detail.category === RecordCategory.inventory && 盘点仓库(签名):} - {record.map((item) => ( - <> - - - {annotate && item.commentIndex && {item.commentIndex + ':' + item.comment}} - - - ))} {detail.pdfExplain} @@ -525,7 +512,13 @@ const PreviewDocument = ({ ); }; -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(); diff --git a/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/settlements-document.tsx b/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/settlements-document.tsx index 8f48b3aff..96bf7e941 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/settlements-document.tsx +++ b/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/settlements-document.tsx @@ -222,7 +222,7 @@ const PreviewDocument = ({ - {contracts.first_party?.name ?? `${PromptText.noContractedCompany}`} + {contracts.project?.associated_company?.name ?? `${PromptText.noContractedCompany}`} 对账单 客户各项费用明细 @@ -231,7 +231,7 @@ const PreviewDocument = ({ 承租单位: - {contracts.party_b?.name} + {contracts?.project?.company?.name} 合同编号: @@ -259,7 +259,7 @@ const PreviewDocument = ({ 项目联系人: {contracts.project?.contacts.map((contact) => contact.name + ' ' + contact.phone).join(' ')} - 制表人:{contracts.operator.nickname} + 经办人:{contracts.operator.nickname} @@ -412,23 +412,21 @@ const PreviewDocument = ({ 备注:{contracts.project?.comment} + + 出租单位: + {contracts.project?.associated_company?.name ?? `${PromptText.noContractedCompany}`} + - 承租单位:{contracts.party_b?.name} - 出租单位:{contracts.first_party?.name} + 制表人: + 审核人: + 验收: 承租单位项目经理: - 出租单位审核人: - - - 承租单位材料负责人: - 出租对账人: - - - 签署日期: - 签署日期: + 材料负责人: + 出租单位代表人: diff --git a/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/waybills-document.tsx b/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/waybills-document.tsx index ae1501682..f52896cf3 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/waybills-document.tsx +++ b/packages/plugins/@hera/plugin-rental/src/server/pdf-documents/waybills-document.tsx @@ -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 - {waybill.payer_company} {waybill.payer_name} + {waybill.payer?.company?.name} {waybill.payer?.name} - {waybill.payee_account_name} + {waybill.payee_account?.name} - {waybill.payee_account_bank} {waybill.payee_account_number} + {waybill.payee_account?.bank} {waybill.payee_account?.number} @@ -365,14 +368,14 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an 发货方单位 - {waybill.shipper_company} {waybill.shipper_name} + {waybill.out_stock?.company?.name} {waybill.out_stock?.name} - {waybill.shipper_contact} + {waybill.shipper_contact?.name} - {waybill.shipper_contact_phone} + {waybill.shipper_contact?.phone} @@ -380,7 +383,7 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an 发货方地址 - {waybill.shipper_address} + {waybill.out_stock?.address} @@ -393,14 +396,14 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an - {waybill.consignee_company} {waybill.consignee_name} + {waybill.in_stock?.company?.name} {waybill.in_stock?.name} - {waybill.consignee_contact} + {waybill.consignee_contact?.name} - {waybill.consignee_contact_phone} + {waybill.consignee_contact?.phone} @@ -408,7 +411,7 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an 收货方地址 - {waybill.consignee_address} + {waybill.in_stock?.address} @@ -418,7 +421,7 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an 承运方单位 - {waybill.carrier} + {waybill.carrier?.name} @@ -428,15 +431,15 @@ const PreviewDocument = ({ waybill, settings }: { waybill: Waybill; settings: an 驾驶员 - {waybill.driver} + {waybill.driver?.name} 身份证 - {waybill.driver_idcard} + {waybill.driver?.id_card} - {waybill.vehicles} + {waybill.record.vehicles.map((item) => item?.number).join(' ')} - {waybill.driver_phone} + {waybill.driver?.phone} diff --git a/packages/plugins/@hera/plugin-rental/src/server/plugin.ts b/packages/plugins/@hera/plugin-rental/src/server/plugin.ts index 5affaba16..7a261b306 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/plugin.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/plugin.ts @@ -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 })), }); diff --git a/packages/plugins/@hera/plugin-rental/src/server/services/contract-rule-service.ts b/packages/plugins/@hera/plugin-rental/src/server/services/contract-rule-service.ts index 402ae3cf9..67403363f 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/services/contract-rule-service.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/services/contract-rule-service.ts @@ -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 { - 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 { 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 { + 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 { + 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; } } diff --git a/packages/plugins/@hera/plugin-rental/src/server/services/contract-service.ts b/packages/plugins/@hera/plugin-rental/src/server/services/contract-service.ts index 7eea0595a..8c32d74ff 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/services/contract-service.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/services/contract-service.ts @@ -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))`, }; diff --git a/packages/plugins/@hera/plugin-rental/src/server/services/record-pdf-service.ts b/packages/plugins/@hera/plugin-rental/src/server/services/record-pdf-service.ts index 7ec68e6f2..1e9442daf 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/services/record-pdf-service.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/services/record-pdf-service.ts @@ -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, }); } } diff --git a/packages/plugins/@hera/plugin-rental/src/server/services/record-service.ts b/packages/plugins/@hera/plugin-rental/src/server/services/record-service.ts index 737a58875..84c16563d 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/services/record-service.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/services/record-service.ts @@ -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 { + 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 { + 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 { // 运输单导致的订单更新不必走以下订单逻辑,减少性能消耗,有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 }); } } } diff --git a/packages/plugins/@hera/plugin-rental/src/server/services/settlement-service.ts b/packages/plugins/@hera/plugin-rental/src/server/services/settlement-service.ts index 524e14c3d..3722156f8 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/services/settlement-service.ts +++ b/packages/plugins/@hera/plugin-rental/src/server/services/settlement-service.ts @@ -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) => { diff --git a/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record.sql b/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record.sql new file mode 100644 index 000000000..891ce6a5b --- /dev/null +++ b/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record.sql @@ -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 diff --git a/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record_fee.sql b/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record_fee.sql new file mode 100644 index 000000000..182d570a1 --- /dev/null +++ b/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record_fee.sql @@ -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 diff --git a/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record_lease.sql b/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record_lease.sql new file mode 100644 index 000000000..8a92a97fc --- /dev/null +++ b/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_record_lease.sql @@ -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 + ) diff --git a/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_waybills.sql b/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_waybills.sql index d5c9329d5..d29f275bf 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_waybills.sql +++ b/packages/plugins/@hera/plugin-rental/src/server/sqls/pdf_waybills.sql @@ -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 diff --git a/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_calc.sql b/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_calc.sql new file mode 100644 index 000000000..201d44314 --- /dev/null +++ b/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_calc.sql @@ -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 diff --git a/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_select.sql b/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_select.sql index b41c0528e..7fe182edf 100644 --- a/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_select.sql +++ b/packages/plugins/@hera/plugin-rental/src/server/sqls/settlement_select.sql @@ -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, ( diff --git a/packages/plugins/@hera/plugin-rental/src/utils/constants.ts b/packages/plugins/@hera/plugin-rental/src/utils/constants.ts index 953e51bbb..8f4a4201a 100644 --- a/packages/plugins/@hera/plugin-rental/src/utils/constants.ts +++ b/packages/plugins/@hera/plugin-rental/src/utils/constants.ts @@ -84,6 +84,18 @@ export enum SourcesType { * 3 出入库量 */ inAndOut = '3', + /** + * 4 出库单数 + */ + outboundNumber = '4', + /** + * 5 入库单数 + */ + inboundNumber = '5', + /** + * 6 出入库单数 + */ + inAndOutNumber = '6', } export enum PromptText {