98lxh 4 månader sedan
förälder
incheckning
433dc79a93
19 ändrade filer med 1806 tillägg och 865 borttagningar
  1. 0 67
      src/views/invoiceInOut/provisionOut/components/BaseForm/index.vue
  2. 50 0
      src/views/invoiceInOut/provisionOut/components/ChildGoodEditModal.vue
  3. 76 0
      src/views/invoiceInOut/provisionOut/components/ChooseGoodModal/config/columns.ts
  4. 102 0
      src/views/invoiceInOut/provisionOut/components/ChooseGoodModal/config/content.config.ts
  5. 33 0
      src/views/invoiceInOut/provisionOut/components/ChooseGoodModal/config/search.config.ts
  6. 69 0
      src/views/invoiceInOut/provisionOut/components/ChooseGoodModal/index.vue
  7. 179 127
      src/views/invoiceInOut/provisionOut/components/NotOrderImportModal/columns-config.ts
  8. 48 234
      src/views/invoiceInOut/provisionOut/components/NotOrderImportModal/index.vue
  9. 76 0
      src/views/invoiceInOut/provisionOut/components/NotOrderImportModal/validator.ts
  10. 173 115
      src/views/invoiceInOut/provisionOut/components/OrderImportModal/columns-config.ts
  11. 37 218
      src/views/invoiceInOut/provisionOut/components/OrderImportModal/index.vue
  12. 76 0
      src/views/invoiceInOut/provisionOut/components/OrderImportModal/validator.ts
  13. 284 19
      src/views/invoiceInOut/provisionOut/config/columns.ts
  14. 268 34
      src/views/invoiceInOut/provisionOut/config/content.config.ts
  15. 1 27
      src/views/invoiceInOut/provisionOut/config/search.config.ts
  16. 33 0
      src/views/invoiceInOut/provisionOut/config/shared.ts
  17. 224 13
      src/views/invoiceInOut/provisionOut/detail.vue
  18. 77 11
      src/views/invoiceInOut/provisionOut/index.vue
  19. 0 0
      src/views/invoiceInOut/provisionOut/出入库管理

+ 0 - 67
src/views/invoiceInOut/provisionOut/components/BaseForm/index.vue

@@ -1,67 +0,0 @@
-<script setup lang="ts">
-import { ref } from 'vue';
-import { goodColumns } from '../../config/columns';
-import { ElButton, ElForm } from 'element-plus';
-import { rules } from '../rules';
-import BasicDescriptions from '/@/components/BasicDescriptions';
-import { UnitInput } from '/@/components/Input';
-import { useRenderIcon } from '/@/components/ReIcon/src/hooks';
-
-const formData = ref({
-    isToAdjust: '',
-    amount: ''
-})
-
-const formRef = ref<InstanceType<typeof ElForm> | null>(null)
-
-
-async function handleSubmit() {
-  try {
-      await formRef.value?.validate()
-  } catch (err) {
-      console.log(err)
-  }
-}
-</script>
-
-<template>
-    <ElForm label-width="140px" :model="formData" ref="formRef" :rules="rules">
-        <ElFormItem label="计提明细">
-            <BasicDescriptions style="width: 100%" :columns="goodColumns" :data="{}" />
-        </ElFormItem>
-
-        <ElFormItem label="关联凭证">
-            <ElTable size="small" border>
-                <!-- <ElTableColumn label="凭证编号" /> -->
-                <ElTableColumn label="商品编号" />
-                <ElTableColumn label="商品名称" />
-                <ElTableColumn label="账面数量" />
-                <ElTableColumn label="目标商品比例" />
-                <ElTableColumn label="关联数量" />
-                <ElTableColumn label="税前单价" />
-                <ElTableColumn label="税后单价" />
-                <ElTableColumn width="60px">
-                    <template #label>
-                        操作
-                        <ElButton :icon="useRenderIcon('add')" />
-                    </template>
-                </ElTableColumn>
-            </ElTable>
-        </ElFormItem>
-
-        <ElFormItem label="调整金额" prop="amount">
-            <UnitInput v-model="formData.amount" placeholder="调整金额" disabled unit="元" />
-        </ElFormItem>
-
-        <ElFormItem label="添加至调整单" prop="isToAdjust">
-            <ElSelect v-model="formData.isToAdjust" placeholder="添加至调整单">
-                <ElOption label="是" value="1" />
-                <ElOption label="否" value="0" />
-            </ElSelect>
-        </ElFormItem>
-
-        <div class="flex w-full justify-end">
-            <ElButton type="primary" @click="handleSubmit">保存</ElButton>
-        </div>
-    </ElForm>
-</template>

+ 50 - 0
src/views/invoiceInOut/provisionOut/components/ChildGoodEditModal.vue

@@ -0,0 +1,50 @@
+<script setup lang="ts">
+import { useVModel } from '@vueuse/core';
+import { ElDialog } from 'element-plus';
+import { goodColumns } from '../config/columns';
+import BasicDescriptions from "/@/components/BasicDescriptions";
+
+import { NumberInput } from "/@/components/Input"
+import { ref, watch} from 'vue';
+
+const props = defineProps<{ 
+    visible: boolean;
+    index: any; 
+    data: any; 
+}>()
+
+const emit = defineEmits(['submit'])
+const visible = useVModel(props, 'visible')
+
+const child_num = ref(0)
+
+
+watch(() => visible.value, () => {
+    if(!visible.value){  return }
+    child_num.value = props.data.goodNum
+})
+
+function handleSubmit(){
+    emit('submit', props.index, { ...props.data, goodNum: child_num.value })    
+    visible.value = false
+}
+</script>
+
+<template>
+    <ElDialog
+       v-model="visible"
+       title="编辑商品" 
+       center
+    >
+        <BasicDescriptions style="overflow: hidden;" v-if="props.data" :data="props.data" :columns="goodColumns" />
+
+        <div class="flex justify-between mt-[10px]">
+            <div class="flex items-center">
+                <p style="width: 110px">操作数量:</p>
+                <NumberInput v-model="child_num" placeholder="操作数量" />
+            </div>
+
+            <ElButton type="primary" @click="handleSubmit">保存</ElButton>
+        </div>
+    </ElDialog>
+</template>

+ 76 - 0
src/views/invoiceInOut/provisionOut/components/ChooseGoodModal/config/columns.ts

@@ -0,0 +1,76 @@
+import { render } from "nprogress";
+import { cg_order_type_options, statusList } from "/@/utils/status";
+import { h } from "vue";
+import { ElTag } from "element-plus";
+
+export const goodColumns = [
+  {
+    span: 12,
+    field: 'seller_name',
+    label: '销售方公司'
+  },
+  {
+    span: 12,
+    field: 'buyer_name',
+    label: '购买方公司'
+  },
+  {
+    span: 12,
+    field: 'goodName',
+    label: '订单商品名称'
+  },
+  {
+    span: 12,
+    field: 'inv_good_name',
+    label: '发票商品名称'
+  },
+  {
+    span: 6,
+    label: '商品类型',
+    render(_, { good_type }){
+      return h(ElTag, { size: 'small' }, {
+        default: () => cg_order_type_options.find(item => item.value == good_type)?.label || '--'
+      })
+    }
+  },
+  {
+    span: 6,
+    field: 'good_code',
+    label: '商品代码'
+  },
+  {
+    span: 6,
+    field: 'spec',
+    label: '规格'
+  },
+  {
+    span: 6,
+    field: 'unit',
+    label: '单位'
+  },
+  {
+    span: 6,
+    field: 'unit_price',
+    label: '税前单价'
+  },
+  {
+    span: 6,
+    field: 'subunit_price',
+    label: '税后单价'
+  },
+  {
+    span: 6,
+    field: 'unit_weight',
+    label: '重量'
+  },
+  {
+    span: 6,
+    field: 'spectral',
+    label: '分光'
+  },
+  {
+    field: 'cat_name',
+    label: '进项类目',
+    span: 24
+  }
+]

+ 102 - 0
src/views/invoiceInOut/provisionOut/components/ChooseGoodModal/config/content.config.ts

@@ -0,0 +1,102 @@
+import { ContentConfig } from "/@/components/PageContent";
+import { httpList } from "/@/api/invoiceInOut/productManager";
+
+import { cg_order_type_options, statusList } from "/@/utils/status";
+import { ElTag } from "element-plus";
+import { h } from "vue";
+
+const columns = [
+  {
+    width: 40,
+    type: 'selection',
+    fixed: 'left'
+  },
+  {
+    label: "商品编码",
+    prop: "skuCode",
+    width: 150
+  },
+  {
+    label: "销售方公司",
+    prop: "seller_name",
+    width: 160
+  },
+  {
+    label: "购买方公司",
+    prop: "buyer_name",
+    width: 160
+  },
+  {
+    label: '商品类型',
+    width: 100,
+    cellRenderer({ row }){
+      return h(ElTag, { size: 'small' }, {
+        default: () => cg_order_type_options.find(item => item.value == row.good_type)?.label || '--'
+      })
+    }
+  },
+  {
+    label: '商品名称',
+    prop: 'goodName',
+    width: 150
+  },
+  {
+    label: "规格",
+    prop: "spec",
+    width: 80
+  },
+  {
+    label: "单位",
+    prop: "unit",
+    width: 80
+  },
+  {
+    label: "状态",
+    prop: "status",
+    width: 80,
+    cellRenderer({ row }){
+      return h(ElTag, { size: 'small', type: statusList.find(item => item.value == row.status)?.type || 'warning' }, {
+        default: () => statusList.find(item => item.value == row.status)?.label || '--'
+      })
+    }
+  },
+  {
+    label: '税前单价',
+    prop: 'subunit_price',
+    width: 100
+  },
+  {
+    label: '税后单价',
+    prop: 'unit_price',
+    width: 100
+  },
+  {
+    label: '重量',
+    prop: 'unit_weight',
+    width: 80
+  },
+  {
+    label: '分光',
+    prop: 'spectral',
+    width: 80
+  },
+  {
+    label: '创建人',
+    prop: 'apply_name',
+    width: 80
+  },
+  {
+    label:'创建时间',
+    prop: 'create_time',
+    width: 140
+  }
+];
+
+const contentConfig: ContentConfig = {
+  columns,
+  apis: { 
+    httpList: (parameter = {}) => httpList({ ...parameter, status: '1' })
+  }
+};
+
+export default contentConfig;

+ 33 - 0
src/views/invoiceInOut/provisionOut/components/ChooseGoodModal/config/search.config.ts

@@ -0,0 +1,33 @@
+import { FormConfig } from "/@/components/PageSearch";
+import { goodTypeOptions } from "/@/utils/status";
+
+const searchFormConfig: FormConfig = {
+  formItems: [
+    {
+      field: 'skuCode',
+      type: 'input',
+      placeholder: '商品编码'
+    },
+    {
+      field: 'good_type',
+      type: 'select',
+      options: goodTypeOptions,
+      placeholder: '商品类型'
+    },
+    {
+      field: "timer",
+      type: "date_picker",
+      span: 7,
+      label: '创建时间',
+      otherOptions: {
+        type: "daterange",
+        startProp: "start",
+        endProp: "end",
+        startPlaceholder: "开始时间",
+        endPlaceholder: "结束时间"
+      }
+    }
+  ]
+};
+
+export default searchFormConfig;

+ 69 - 0
src/views/invoiceInOut/provisionOut/components/ChooseGoodModal/index.vue

@@ -0,0 +1,69 @@
+<script setup lang="ts">
+import { usePageSearch, type PageHooks, type PageEvents } from "/@/hooks/page";
+import contentConfig from "./config/content.config";
+import { ElDialog, ElMessage } from "element-plus";
+import searchConfig from "./config/search.config";
+import { useVModel } from "@vueuse/core";
+import { useRouter } from "vue-router";
+import { ref } from "vue";
+
+const props = defineProps<{ visible: boolean; otherSearchParameter: any }>()
+const emit = defineEmits(['submit'])
+
+const visible = useVModel(props, 'visible')
+const router = useRouter();
+
+const hooks: PageHooks = {
+  pageSearchHook: () => usePageSearch(undefined, undefined, searchConfig)
+};
+
+const selection= ref([])
+
+const events: PageEvents = {
+  content: {
+    preview: ({ id }) => router.push("/invoiceInOut/productManagerDetail?id=" + id),
+    create: () => router.push('/invoiceInOut/productManagerDetail')
+  }
+};
+
+function handleSubmit(){
+   if(selection.value.length === 0){
+       ElMessage.warning('请选择至少一个商品')
+       return
+   }
+
+   const result = []
+   selection.value.forEach(single => {
+      if (single.ProductsCombind.length === 0) { 
+        result.push({ ...single, goodNum: 1 })
+      } else {
+        single.ProductsCombind.forEach(child => {
+          result.push({ ...single, goodNum: 1 ,childCode: child.products.skuCode, childNum: single.child_num, childActionNum: single.child_num })
+        })
+      }
+   })
+
+   visible.value = false
+   emit('submit', result)
+}
+</script>
+
+<template>
+    <ElDialog class="relative" v-model="visible" title="选择商品" center top="10px">
+      <PageContainer
+        v-if="visible"
+        :hooks="hooks"
+        :events="events"
+        :searchConfig="searchConfig"
+        :contentConfig="contentConfig"
+        :isPageStart="true"
+        :other-search-parameter="otherSearchParameter"
+        @content-select-change="values => selection = values.map(item => ({ ...item, child_num: '1', child_id: item.id }))"
+      />
+      <ElButton 
+        class="absolute bottom-[30px] right-[20px]" 
+        type="primary"
+        @click="handleSubmit"
+       >保存 </ElButton>
+    </ElDialog>
+</template>

+ 179 - 127
src/views/invoiceInOut/provisionOut/components/NotOrderImportModal/columns-config.ts

@@ -1,35 +1,3 @@
-const initheaders = [
-  "买方公司编码",
-  "买方公司名称",
-  "卖方公司编码",
-  "卖方公司名称",
-  "对账编码",
-  "发票类型",
-  "发票代码",
-  "发票号码",
-  "发票税前金额",
-  "发票税后金额",
-  "开票日期",
-  "校验码"
-];
-
-export const mapProp = {
-  value0: "companyNo",
-  value1: "companyName",
-  value2: "supplierNo",
-  value3: "supplierName",
-  value4: "payNo",
-  value5: "invoiceType",
-  value6: "invoiceCode",
-  value7: "invoiceNumber",
-  value8: "inv_subtotal_amount",
-  value9: "inv_total",
-  value10: "open_time",
-  value11: "checkNumber"
-};
-
-export const requireHeaders = [];
-
 const columns = [
   {
     type: "index",
@@ -39,214 +7,298 @@ const columns = [
   },
   {
     label: '业务编号',
-    prop: 'TODO1',
-    minWidth: '100px'
+    prop: 'invoiceCode',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '类型',
-    prop: 'TODO2',
-    minWidth: '100px'
+    prop: 'type',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label:'订单来源',
-    prop: 'TODO3',
-    minWidth: '100px'
+    prop: 'source',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '销售方公司纳税识别号',
-    prop: 'TODO4',
-    minWidth: '140px'
+    prop: 'seller_code',
+    minWidth: '160px',
+    defaultData: '',
+    required: true
   },
   {
     label: '销售方公司',
-    prop: 'TODO5',
-    minWidth: '100px'
+    prop: 'seller_name',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '订单编号',
-    prop: 'TODO6',
-    minWidth: '120px'
+    prop: 'orderCode',
+    minWidth: '120px',
+    defaultData: '',
+    required: true
   },
   {
     label: '订单主单号',
-    prop: 'TODO7',
-    minWidth: '120px'
+    prop: 'cxCode',
+    minWidth: '120px',
+    defaultData: '',
+    required: true
   },
   {
     label: '商品编号',
-    prop: 'TODO8',
-    minWidth: '100px'
+    prop: 'goodNo',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '商品名称',
-    prop: 'TODO9',
-    minWidth: '100px'
+    prop: 'goodName',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '单位',
-    prop: 'TODO10',
-    minWidth: '80px'
+    prop: 'unit',
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   },
   {
     label: '商品数量',
-    prop: 'TODO11',
-    minWidth: '110px'
-  },
-  {
-    label: '商品数量',
-    prop: 'TODO11',
-    minWidth: '110px'
+    prop: 'num',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
     label:'商品单价',
-    prop: 'TODO12',
-    minWidth: '110px'
+    prop: 'goodPrice',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
     label: '订单总金额',
-    prop: 'TODO13',
-    minWidth: '110px'
+    prop: 'totalPrice',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税目',
-    prop: 'TODO14',
-    minWidth: '80px'
+    prop: 'cat_code',
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税目名称',
-    prop: 'TODO15',
-    minWidth: '100px'
+    prop: 'cat_name',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
+  },
+  {
+    label: '税率',
+    prop: 'tax',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '关联金额',
-    prop: 'TODO16',
-    minWidth: '100px'
+    prop: 'inv_fee',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '购买方公司名称',
-    prop: 'TODO17',
-    minWidth: '110px'
+    prop: 'inv_buyer_name',
+    minWidth: '130px',
+    defaultData: '',
+    required: true
   },
   {
     label: '购买方公司纳税识别号',
-    prop: 'TODO18',
-    minWidth: '140px'
+    prop: 'inv_buyer_code',
+    minWidth: '170px',
+    defaultData: '',
+    required: true
   },
   {
     label: '发票号码',
-    prop: 'TODO19',
-    minWidth: '100px'
+    prop: 'inv_number',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '发票类型',
-    prop: 'TODO20',
-    minWidth: '100px'
+    prop: 'inv_type',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '销售方公司名称',
-    prop: 'TODO21',
-    minWidth: '110px'
+    label: '发票销售方公司',
+    prop: 'inv_seller_name',
+    minWidth: '130px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '销售方公司纳税号',
-    prop: 'TODO',
-    minWidth: '140px'
+    label: '发票销售方公司纳税号',
+    prop: 'inv_seller_code',
+    minWidth: '170px',
+    defaultData: '',
+    required: true
   },
   {
     label: '发票明细ID',
-    prop: 'TODO',
-    minWidth: '110px'
+    prop: 'inv_item_id',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
     label: '货物或应税劳务、服务名称',
-    prop: 'TODO',
-    minWidth: '200px'
+    prop: 'inv_good_name',
+    minWidth: '200px',
+    defaultData: '',
+    required: true
   },
   {
     label: '类目编号',
-    prop: 'TODO',
-    minWidth: '90px'
+    prop: 'inv_cat_code',
+    minWidth: '90px',
+    defaultData: '',
+    required: true
   },
   {
     label: '规格型号',
-    prop: 'TODO',
-    minWidth: '110px'
+    prop: 'inv_spec',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '单位',
-    prop: 'TODO',
-    minWidth: '80px'
+    label: '发票商品单位',
+    prop: 'inv_unit',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '数量',
-    prop: 'TODO',
-    minWidth: '80px'
+    label: '发票商品数量',
+    prop: 'inv_num',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税前单价',
-    prop: 'TODO',
-    minWidth: '100px'
+    prop: 'inv_subprice',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税前总价',
-    prop: 'TODO',
-    minWidth: '100px'
+    prop: 'inv_subtotal',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '税率',
-    prop: 'TODO',
-    minWidth: '80px'
+    label: '发票税率',
+    prop: 'inv_tax',
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税后单价',
-    prop: 'TODO',
-    minWidth: '100px'
+    prop: 'inv_price',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税后总额',
-    prop: 'TODO',
-    minWidth: '100px'
+    prop: 'inv_total',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '类目编号状态',
-    prop: 'TODO',
-    minWidth: '100px'
+    prop: 'TODO34',
+    minWidth: '100px',
+    defaultData: '',
+    required: false
   },
   {
     label: '税率状态',
-    prop: 'TODO',
-    minWidth: '100px'
+    prop: 'TODO35',
+    minWidth: '100px',
+    defaultData: '',
+    required: false
   },
   {
     label: '备注',
-    prop: 'TODO',
-    minWidth: '80px'
+    prop: 'remark',
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '商品编号',
-    prop: 'TODO',
-    minWidth: '90px'
+    label: '关联商品ID',
+    prop: 'relaGoodNo',
+    minWidth: '120px',
+    defaultData: '',
+    required: true
   },
   {
     label: '操作数量',
-    prop: 'TODO',
-    minWidth: '90px'
+    prop: 'relaGoodNum',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '子商品编号',
-    prop: 'TODO',
-    minWidth: '100px'
+    prop: 'childCode',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '子商品操作数量',
-    prop: 'TODO',
-    minWidth: '110px'
+    prop: 'childNum',
+    minWidth: '120px',
+    defaultData: '',
+    required: true
   }
 ]
 
+const notOrderTemplate = [columns.slice(1).reduce((prev, current) => ({ ...prev, [current.label]: current.defaultData ? current.defaultData : '' }), {})]
+
+export const mapLabelToProp = columns.reduce((prev, current) => ({  ...prev, [current.label]: current.prop }), {})
+export const mapPropertyToLabel = columns.reduce((prev, current) => ({ ...prev, [current.prop]: current.label }), {})
 
-const notOrderTemplate = [columns.slice(1).reduce((prev, current) => {
-  return {...prev, [current.label] : current.detaultData ? current.defaultData : ''}
-}, {})]
+export const requiredProps = columns.filter(column => !!column.required).map(item => item.prop)
 
-export { initheaders, columns, notOrderTemplate };
+export { columns, notOrderTemplate };

+ 48 - 234
src/views/invoiceInOut/provisionOut/components/NotOrderImportModal/index.vue

@@ -1,38 +1,24 @@
 <script setup lang="ts">
-import { ref, h } from "vue";
-import { ElButton, ElDialog, ElMessage,ElMessageBox } from "element-plus";
+import { ref } from "vue";
+import { ElButton, ElDialog, ElMessage } from "element-plus";
 import { execlUpload } from "/@/components/execlUpload";
-import { httpBatchAdd } from "/@/api/purchase/ticketReturn";
 import { useResponseHandle } from "/@/hooks";
-import { useCompany } from "/@/hooks/core/useCompany";
-import { cg_inv_type_list } from "/@/utils/status";
 import { useVModel } from "@vueuse/core";
-import dayjs from "dayjs"
 
-import {
-  initheaders,
-  columns,
-  mapProp,
-  requireHeaders
-} from "./columns-config";
+import { mapLabelToSource, mapLabelToType } from "../../config/shared";
+import { httpAdd } from "/@/api/invoiceInOut/inOutManager";
+import { columns, mapLabelToProp } from "./columns-config";
+import { mapLabelToInvtype } from "/@/utils/status";
+import { isImportDataValid } from "./validator";
 
 
 const loading = ref(false);
 const tableData = ref([]);
 
+const emit = defineEmits(["refresh"]);
 const props = defineProps<{ visible: boolean }>()
-const emit = defineEmits(["onSuccess"]);
-
-const allTypes = cg_inv_type_list.map(({ label }) => label);
-const row = ref(1);
 
 const visible = useVModel(props, 'visible')
-
-const createInvErrorMessage = (row: string) =>
-  `导入数据第 ${row} 行 发票类型格式不正确,发票类型必须为${allTypes.join(",")}`;
-
-const { currentCompany } = useCompany();
-
 const responseHandle = useResponseHandle();
 
 const Uploadsuccess = ({ results, header }) => {
@@ -44,12 +30,13 @@ const Uploadsuccess = ({ results, header }) => {
   }
 
   let headok = true;
+
   if (header.length !== columns.length - 1) {
     headok = false;
   } else {
     columns.slice(1).forEach((si, sii) => {
-      if (si.label !== header[sii]) {
-        headok = false;
+      if (si.label !== header[sii]) { 
+        headok = false; 
       }
     });
   }
@@ -62,151 +49,19 @@ const Uploadsuccess = ({ results, header }) => {
 
   tableData.value = [];
 
-  for (const v1 of results) {
-    const b = Object.values(v1);
-    let model = {};
-    b.forEach((si, sii) => { model["value" + sii] = si + ""; });
-    tableData.value.push(model);
-    row.value = row.value + 1;
-  }
-
-    const data = [];
-   tableData.value.forEach((key, index) => {
-      const obj: Record<string, string> = {};
-
-      for (let i in key) {
-        const prop = mapProp[i];
-        const value = key[i];
-        obj[prop] = value;
-     }
-      
-      data.push(obj);
-    });
-
-
-    const typeErrors: string[] = [];
-    const checkErrors: string[] = [];
-    const codeErrors: string[] = [];
-    const numberErrors: string[] = [];
-    const priceErrors: string[] = [];
-    const fullyElectionicPriceErrors: string[] = [];
-
-    
-  const openDateErrors: string[] = [];
-  const now = dayjs(new Date()).format('YYYY-MM-DD');
-
-    data.forEach((row, index) => {
-      const source = row.invoiceType.trim();
-      const target = cg_inv_type_list.find(({ label }) => label === source);
+  const result = []
+  for(const tableItem of results){
 
-      if (!target) {
-        typeErrors.push(String(index + 1));
-      } else {
-        row.invoiceType = target.value;
+    const item = {}
+    Object.keys(tableItem).forEach(label => {
+      const prop = mapLabelToProp[label]
+      item[prop] = tableItem[label]
+    })
 
-        if (
-          (target.value === "normal" || target.value === "electronic") &&
-          !row.checkNumber
-        ) {
-          checkErrors.push(String(index + 1));
-        }
-      }
-
-      const isFullyElectionic = row.invoiceType === 'fully_digitalized_special_electronic' || row.invoiceType === 'fully_digitalized_normal_electronic'
-
-      if(!row.open_time || (row.open_time && !dayjs(row.open_time).isSame(dayjs(now)) && !dayjs(row.open_time).isBefore(dayjs(now)))){
-        openDateErrors.push(String(index + 1))
-      }
-
-      const reg = /^\d+(\.\d+)?$/;
-
-      if (!reg.test(row.invoiceCode) && !isFullyElectionic) {
-        codeErrors.push(String(index + 1));
-      }
-
-      if (!reg.test(row.invoiceNumber)) {
-        numberErrors.push(String(index + 1));
-      }
-
-      if ((!reg.test(row.inv_subtotal_amount) || Number(row.inv_subtotal_amount) < 0) && !isFullyElectionic) {
-        priceErrors.push(String(index + 1));
-      }
-
-      if ((!reg.test(row.inv_total) || Number(row.inv_total) < 0) && isFullyElectionic) {
-        fullyElectionicPriceErrors.push(String(index + 1));
-      }
-    });
-
-    if (typeErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: createInvErrorMessage(typeErrors.join(","))
-      });
-      loading.value = false;
-      return;
-    }
-
-    if (checkErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: `第 ${checkErrors.join(",")} 行,校验码不能为空。`
-      });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-    if (codeErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: `第 ${codeErrors.join(",")} 行,发票代码必须为数字。`
-      });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-
-    if (numberErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: `第 ${numberErrors.join(",")} 行,发票号码必须为数字。`
-      });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-    if (priceErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: `第 ${priceErrors.join(",")} 行,发票税前金额必须为数字且不能是负数`
-      });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-    if (fullyElectionicPriceErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: `第 ${fullyElectionicPriceErrors.join(",")} 行,发票税后金额必须为数字且不能是负数`
-      });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-    if (openDateErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: `第 ${openDateErrors.join(",")} 行,开票日期不能为空且不能超过当前日期`
-      });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
+    result.push(item)
+  }
 
+  if(isImportDataValid(result)){ tableData.value = result }
   loading.value = false;
 };
 
@@ -215,59 +70,31 @@ const handleSubmit = async () => {
   try {
     if (loading.value) return;
     loading.value = true;
-    const data = [];
 
-    tableData.value.forEach((key, index) => {
-      const obj: Record<string, string> = {};
-
-      for (let i in key) {
-        const prop = mapProp[i];
-        const value = key[i];
-        obj[prop] = value;
-      }
-      data.push(obj);
-    });
-
-    data.forEach((row, index) => {
-      const source = row.invoiceType.trim();
-      const target = cg_inv_type_list.find(({ label }) => label === source);
-      row.invoiceType = target.value;
-    });
-
-
-    const buyers = data.map(({ supplierNo }) => supplierNo);
-    const setBuyers = [...new Set(buyers)];
-
-    if (setBuyers.length > 1) {
-      ElMessage.error("卖方公司编码不一致");
-      loading.value = false;
-      return;
-    }
-
-    const companyNo = data[0].supplierNo;
-
-    if(companyNo != currentCompany.value.companyNo){
-      ElMessage.error("卖方公司编码与当前选中的公司不一致");
-      loading.value = false;
-      return;
-    }
-
-    data.forEach(item => {
-      const isFullyElectionic = item.invoiceType === 'fully_digitalized_special_electronic' || item.invoiceType === 'fully_digitalized_normal_electronic'
-      if(isFullyElectionic){ item.inv_subtotal_amount = item.inv_total }
-      
-      delete item['inv_total']
-      delete item["supplierNo"];
-      delete item["supplierName"];
-      delete item["companyName"];
-      delete item["companyNo"];
-    });
-    const { code, message,data: _d } = await httpBatchAdd({
-      list: data,
-      companyNo,
-      is_comon: '0'
-      // relaComNo: currentCompany.value.companyNo
-    });
+    const list = [];
+
+    tableData.value.forEach(item => {
+      list.push({ 
+        ...item,
+        inv_type: mapLabelToInvtype[item.inv_type],
+        source: mapLabelToSource[item.source], 
+        type: mapLabelToType[item.type],
+        buyer_name: item.inv_buyer_name,
+        buyer_code: item.inv_buyer_code,
+        platform_type: "1",
+        goodType: "1",
+        channel: "2",
+        relaArr: [{  
+          id: item.relaGoodNo, 
+          num: item.relaGoodNum 
+        }]
+      })
+    })
+
+    // console.log(list)
+    // return
+
+    const { code, message,data: _d } = await httpAdd({ list });
 
     loading.value = false;
 
@@ -277,21 +104,8 @@ const handleSubmit = async () => {
       noMessage: false,
       handler: () => {
         ElMessage.success("数据导入成功!");
-        emit("onSuccess");
+        emit("refresh");
         visible.value = false;
-      },
-       onError() {
-        if (Number(code) === 10004) {
-          ElMessageBox({
-            title: '以下对账单不符合要求',
-            message: h('div', null, [
-              h('p', null, '当前功能仅支持业务类型为采购订单的对账单,以下通用订单类型的对账单不支持该功能:'),
-              h('p', null, _d.join('、')),
-            ])
-          })
-         }else {
-          ElMessage.warning(message)
-         }
       }
     });
   } catch (err) { 
@@ -305,8 +119,8 @@ const cancel = () => {
 
 <template>
   <ElDialog
-    :close-on-click-modal="false"
     v-model="visible"
+    :close-on-click-modal="false"
     title="非订单商品导入"
     width="1040px"
     top="8vh"
@@ -332,7 +146,7 @@ const cancel = () => {
         :key="sii"
       >
         <template #header>
-          <span v-if="!requireHeaders.includes(mapProp[si.prop]) || si.label === '序号' ">
+          <span v-if="!si.required">
             {{ si.label }}
           </span>
           

+ 76 - 0
src/views/invoiceInOut/provisionOut/components/NotOrderImportModal/validator.ts

@@ -0,0 +1,76 @@
+import { h } from "vue"
+import { ElMessageBox } from "element-plus"
+import { mapPropertyToLabel, requiredProps } from "./columns-config"
+import { mapLabelToSource, mapLabelToType, sourceOptions, typeOptions } from "../../config/shared"
+import { mapLabelToInvtype, xs_inv_type_list } from "/@/utils/status"
+
+function onDisplayErrorMessage(errors) {
+    ElMessageBox({
+        type: 'warning',
+        title: '数据校验失败',
+        message: h('div', { style: 'display: flex, flex-direction: column' }, {
+            default: () => Object.keys(errors).reduce((prev, index) => {
+                return [
+                    ...prev,
+                    h('p', {}, '第' + index  + '行,' + errors[index])
+                ]
+            }, [])
+        })
+    })
+}
+
+
+export function isValidRequired(results: any[]) {
+    const errors = {}
+
+    let isTypeError = false
+    let isSourceError = false
+    let isInvError = false
+
+    for (const sourceIndex in results) {
+        const item = results[sourceIndex];
+        const index = Number(sourceIndex) + 1;
+
+        for (const key in item) {
+            const value = String(item[key]).trim()
+            if (requiredProps.includes(key) && (!value || !String(value).trim())) {
+                if (errors[index]) {
+                    errors[index] = errors[index] += '、' + mapPropertyToLabel[key]
+                } else {
+                    errors[index] = mapPropertyToLabel[key]
+                }
+            } else if (key === 'type' && !mapLabelToType[value]){
+                isTypeError = true
+            } else if (key === 'source' && !mapLabelToSource[value]) {
+                isSourceError = true
+            } else if (key === 'inv_type' && !mapLabelToInvtype[value]){
+                isInvError = true
+            }
+        }
+
+        if (errors[index]) {
+            errors[index] += '不能为空'
+            if (isTypeError) errors[index] += ' , 类型必须为' + typeOptions.map(({ label }) => label).join('、')
+            if (isSourceError) errors[index] += ' ,订单来源必须为' + sourceOptions.map(({ label }) => label).join('、')
+            if (isInvError) errors[index] += ' ,发票类型必须为' + xs_inv_type_list.map(({ label }) => label).join('、')
+        } else {
+            if (isTypeError) errors[index] = '类型必须为' + typeOptions.map(({ label }) => label).join('、')
+            if (isSourceError) errors[index] = ' 订单来源必须为' + sourceOptions.map(({ label }) => label).join('、')
+            if (isInvError) errors[index] = ' 发票类型必须为' + xs_inv_type_list.map(({ label }) => label).join('、')
+        }
+    }
+
+
+    if (Object.keys(errors).length === 0) {
+        return true
+    } else {
+        onDisplayErrorMessage(errors)
+        return false
+    }
+}
+
+export function isImportDataValid(results: any[]) {
+    /* 校验必填字段是否填入 */
+    if (!isValidRequired(results)) { return false }
+    return true
+}

+ 173 - 115
src/views/invoiceInOut/provisionOut/components/OrderImportModal/columns-config.ts

@@ -1,35 +1,3 @@
-const initheaders = [
-  "买方公司编码",
-  "买方公司名称",
-  "卖方公司编码",
-  "卖方公司名称",
-  "对账编码",
-  "发票类型",
-  "发票代码",
-  "发票号码",
-  "发票税前金额",
-  "发票税后金额",
-  "开票日期",
-  "校验码"
-];
-
-export const mapProp = {
-  value0: "companyNo",
-  value1: "companyName",
-  value2: "supplierNo",
-  value3: "supplierName",
-  value4: "payNo",
-  value5: "invoiceType",
-  value6: "invoiceCode",
-  value7: "invoiceNumber",
-  value8: "inv_subtotal_amount",
-  value9: "inv_total",
-  value10: "open_time",
-  value11: "checkNumber"
-};
-
-export const requireHeaders = [];
-
 const columns = [
   {
     type: "index",
@@ -39,198 +7,288 @@ const columns = [
   },
   {
     label: '业务编号',
-    prop: 'TODO1',
-    minWidth: '100px'
+    prop: 'invoiceCode',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '类型',
-    prop: 'TODO2',
-    minWidth: '100px'
+    prop: 'type',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label:'订单来源',
-    prop: 'TODO3',
-    minWidth: '100px'
+    prop: 'source',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '销售方公司纳税识别号',
-    prop: 'TODO4',
-    minWidth: '140px'
+    prop: 'seller_code',
+    minWidth: '160px',
+    defaultData: '',
+    required: true
   },
   {
     label: '销售方公司',
-    prop: 'TODO5',
-    minWidth: '100px'
+    prop: 'seller_name',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '订单编号',
-    prop: 'TODO6',
-    minWidth: '120px'
+    prop: 'orderCode',
+    minWidth: '120px',
+    defaultData: '',
+    required: true
   },
   {
     label: '订单主单号',
-    prop: 'TODO7',
-    minWidth: '120px'
+    prop: 'cxCode',
+    minWidth: '120px',
+    defaultData: '',
+    required: true
   },
   {
     label: '商品编号',
-    prop: 'TODO8',
-    minWidth: '100px'
+    prop: 'goodNo',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '商品名称',
-    prop: 'TODO9',
-    minWidth: '100px'
+    prop: 'goodName',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '单位',
-    prop: 'TODO10',
-    minWidth: '80px'
-  },
-  {
-    label: '商品数量',
-    prop: 'TODO11',
-    minWidth: '110px'
+    prop: 'unit',
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   },
   {
     label: '商品数量',
-    prop: 'TODO11',
-    minWidth: '110px'
+    prop: 'num',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
     label:'商品单价',
-    prop: 'TODO12',
-    minWidth: '110px'
+    prop: 'goodPrice',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
     label: '订单总金额',
-    prop: 'TODO13',
-    minWidth: '110px'
+    prop: 'totalPrice',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税目',
-    prop: 'TODO14',
-    minWidth: '80px'
+    prop: 'cat_code',
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税目名称',
-    prop: 'TODO15',
-    minWidth: '100px'
+    prop: 'cat_name',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
+  },
+  {
+    label: '税率',
+    prop: 'tax',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '关联金额',
-    prop: 'TODO16',
-    minWidth: '100px'
+    prop: 'inv_fee',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '购买方公司名称',
-    prop: 'TODO17',
-    minWidth: '110px'
+    prop: 'inv_buyer_name',
+    minWidth: '130px',
+    defaultData: '',
+    required: true
   },
   {
     label: '购买方公司纳税识别号',
-    prop: 'TODO18',
-    minWidth: '140px'
+    prop: 'inv_buyer_code',
+    minWidth: '170px',
+    defaultData: '',
+    required: true
   },
   {
     label: '发票号码',
-    prop: 'TODO19',
-    minWidth: '100px'
+    prop: 'inv_number',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '发票类型',
-    prop: 'TODO20',
-    minWidth: '100px'
+    prop: 'inv_type',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '销售方公司名称',
-    prop: 'TODO21',
-    minWidth: '110px'
+    label: '发票销售方公司',
+    prop: 'inv_seller_name',
+    minWidth: '130px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '销售方公司纳税号',
-    prop: 'TODO22',
-    minWidth: '140px'
+    label: '发票销售方公司纳税号',
+    prop: 'inv_seller_code',
+    minWidth: '170px',
+    defaultData: '',
+    required: true
   },
   {
     label: '发票明细ID',
-    prop: 'TODO23',
-    minWidth: '110px'
+    prop: 'inv_item_id',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
     label: '货物或应税劳务、服务名称',
-    prop: 'TODO24',
-    minWidth: '200px'
+    prop: 'inv_good_name',
+    minWidth: '200px',
+    defaultData: '',
+    required: true
   },
   {
     label: '类目编号',
-    prop: 'TODO25',
-    minWidth: '90px'
+    prop: 'inv_cat_code',
+    minWidth: '90px',
+    defaultData: '',
+    required: true
   },
   {
     label: '规格型号',
-    prop: 'TODO27',
-    minWidth: '110px'
+    prop: 'inv_spec',
+    minWidth: '110px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '单位',
-    prop: 'TODO28',
-    minWidth: '80px'
+    label: '发票商品单位',
+    prop: 'inv_unit',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '数量',
-    prop: 'TODO29',
-    minWidth: '80px'
+    label: '发票商品数量',
+    prop: 'inv_num',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税前单价',
-    prop: 'TODO30',
-    minWidth: '100px'
+    prop: 'inv_subprice',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税前总价',
-    prop: 'TODO30',
-    minWidth: '100px'
+    prop: 'inv_subtotal',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
-    label: '税率',
-    prop: 'TODO31',
-    minWidth: '80px'
+    label: '发票税率',
+    prop: 'inv_tax',
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税后单价',
-    prop: 'TODO32',
-    minWidth: '100px'
+    prop: 'inv_price',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '税后总额',
-    prop: 'TODO33',
-    minWidth: '100px'
+    prop: 'inv_total',
+    minWidth: '100px',
+    defaultData: '',
+    required: true
   },
   {
     label: '类目编号状态',
     prop: 'TODO34',
-    minWidth: '100px'
+    minWidth: '100px',
+    defaultData: '',
+    required: false
   },
   {
     label: '税率状态',
     prop: 'TODO35',
-    minWidth: '100px'
+    minWidth: '100px',
+    defaultData: '',
+    required: false
   },
   {
     label: '备注',
-    prop: 'TODO36',
-    minWidth: '80px'
+    prop: 'remark',
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   },
   {
     label: '入账月份',
     prop: 'TODO37',
-    minWidth: '80px'
+    minWidth: '80px',
+    defaultData: '',
+    required: true
   }
 ]
 
 const orderTemplate = [columns.slice(1).reduce((prev, current) => {
-  return { ...prev, [current.label]: current.detaultData ? current.defaultData : '' }
+  return { ...prev, [current.label]: current.defaultData ? current.defaultData : '' }
 }, {})]
 
-export { initheaders, columns, orderTemplate };
+
+export const mapLabelToProp = columns.reduce((prev, current) => ({
+  ...prev,
+  [current.label]: current.prop
+}), {})
+
+
+export const mapPropertyToLabel = columns.reduce((prev, current) => ({
+  ...prev,
+  [current.prop]: current.label
+}), {})
+
+export const requiredProps = columns.filter(column => !!column.required).map(item => item.prop)
+
+export { columns, orderTemplate };

+ 37 - 218
src/views/invoiceInOut/provisionOut/components/OrderImportModal/index.vue

@@ -1,36 +1,25 @@
 <script setup lang="ts">
 import { ref, h } from "vue";
-import { ElButton, ElDialog, ElMessage,ElMessageBox } from "element-plus";
+import { ElButton, ElDialog, ElMessage } from "element-plus";
 import { execlUpload } from "/@/components/execlUpload";
-import { httpBatchAdd } from "/@/api/purchase/ticketReturn";
-import { useCompany } from "/@/hooks/core/useCompany";
-import { cg_inv_type_list } from "/@/utils/status";
 import { useResponseHandle } from "/@/hooks";
 import { useVModel } from "@vueuse/core";
-import dayjs from "dayjs"
 
-import {
-  initheaders,
-  columns,
-  mapProp,
-  requireHeaders
-} from "./columns-config";
+import { columns, mapLabelToProp } from "./columns-config";
+import { isImportDataValid } from "./validator";
+import { httpAdd } from "/@/api/invoiceInOut/inOutManager";
+import { mapLabelToSource, mapLabelToType } from "../../config/shared";
+import { mapLabelToInvtype } from "/@/utils/status";
+
 
 
-const loading = ref(false);
 const tableData = ref([]);
+const loading = ref(false);
 
+const emit = defineEmits(["refresh"]);
 const props = defineProps<{ visible: boolean }>()
-const emit = defineEmits(["onSuccess"]);
 
-const row = ref(1);
-const allTypes = cg_inv_type_list.map(({ label }) => label);
 const visible = useVModel(props, 'visible')
-
-const createInvErrorMessage = (row: string) => `导入数据第 ${row} 行 发票类型格式不正确,发票类型必须为${allTypes.join(",")}`;
-
-const { currentCompany } = useCompany();
-
 const responseHandle = useResponseHandle();
 
 const Uploadsuccess = ({ results, header }) => {
@@ -42,12 +31,11 @@ const Uploadsuccess = ({ results, header }) => {
   }
 
   let headok = true;
+
   if (header.length !== columns.length - 1) {
-    console.log('joined', header, columns)
     headok = false;
   } else {
     columns.slice(1).forEach((si, sii) => {
-    console.log(si.label, header[sii])
       if (si.label !== header[sii]) { 
         headok = false; 
       }
@@ -62,141 +50,19 @@ const Uploadsuccess = ({ results, header }) => {
 
   tableData.value = [];
 
-  for (const v1 of results) {
-    const b = Object.values(v1);
-    let model = {};
-    b.forEach((si, sii) => { model["value" + sii] = si + ""; });
-    tableData.value.push(model);
-    row.value = row.value + 1;
-  }
-
-    const data = [];
-   tableData.value.forEach((key) => {
-      const obj: Record<string, string> = {};
-
-      for (let i in key) {
-        const prop = mapProp[i];
-        const value = key[i];
-        obj[prop] = value;
-     }
-      
-      data.push(obj);
-    });
-
-
-    const typeErrors: string[] = [];
-    const checkErrors: string[] = [];
-    const codeErrors: string[] = [];
-    const numberErrors: string[] = [];
-    const priceErrors: string[] = [];
-    const fullyElectionicPriceErrors: string[] = [];
-
-    
-  const openDateErrors: string[] = [];
-  const now = dayjs(new Date()).format('YYYY-MM-DD');
-
-    data.forEach((row, index) => {
-      const source = row.invoiceType.trim();
-      const target = cg_inv_type_list.find(({ label }) => label === source);
-
-      if (!target) {
-        typeErrors.push(String(index + 1));
-      } else {
-        row.invoiceType = target.value;
-
-        if (
-          (target.value === "normal" || target.value === "electronic") &&
-          !row.checkNumber
-        ) {
-          checkErrors.push(String(index + 1));
-        }
-      }
-
-      const isFullyElectionic = row.invoiceType === 'fully_digitalized_special_electronic' || row.invoiceType === 'fully_digitalized_normal_electronic'
-
-      if(!row.open_time || (row.open_time && !dayjs(row.open_time).isSame(dayjs(now)) && !dayjs(row.open_time).isBefore(dayjs(now)))){
-        openDateErrors.push(String(index + 1))
-      }
-
-      const reg = /^\d+(\.\d+)?$/;
-
-      if (!reg.test(row.invoiceCode) && !isFullyElectionic) {
-        codeErrors.push(String(index + 1));
-      }
-
-      if (!reg.test(row.invoiceNumber)) {
-        numberErrors.push(String(index + 1));
-      }
-
-      if ((!reg.test(row.inv_subtotal_amount) || Number(row.inv_subtotal_amount) < 0) && !isFullyElectionic) {
-        priceErrors.push(String(index + 1));
-      }
-
-      if ((!reg.test(row.inv_total) || Number(row.inv_total) < 0) && isFullyElectionic) {
-        fullyElectionicPriceErrors.push(String(index + 1));
-      }
-    });
-
-    if (typeErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: createInvErrorMessage(typeErrors.join(","))
-      });
-      loading.value = false;
-      return;
-    }
-
-    if (checkErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: `第 ${checkErrors.join(",")} 行,校验码不能为空。`
-      });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-    if (codeErrors.length > 0) {
-      ElMessage({
-        type: "error",
-        message: `第 ${codeErrors.join(",")} 行,发票代码必须为数字。`
-      });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-
-    if (numberErrors.length > 0) {
-      ElMessage({ type: "error", message: `第 ${numberErrors.join(",")} 行,发票号码必须为数字。`});
-
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-    if (priceErrors.length > 0) {
-      ElMessage({ type: "error", message: `第 ${priceErrors.join(",")} 行,发票税前金额必须为数字且不能是负数` });
+  const result = []
+  for(const tableItem of results){
 
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
+    const item = {}
+    Object.keys(tableItem).forEach(label => {
+      const prop = mapLabelToProp[label]
+      item[prop] = tableItem[label]
+    })
 
-    if (fullyElectionicPriceErrors.length > 0) {
-      ElMessage({ type: "error", message: `第 ${fullyElectionicPriceErrors.join(",")} 行,发票税后金额必须为数字且不能是负数` });
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
-
-    if (openDateErrors.length > 0) {
-      ElMessage({ type: "error", message: `第 ${openDateErrors.join(",")} 行,开票日期不能为空且不能超过当前日期`});
-      loading.value = false;
-      tableData.value = [];
-      return;
-    }
+    result.push(item)
+  }
 
+  if(isImportDataValid(result)){ tableData.value = result }
   loading.value = false;
 };
 
@@ -205,58 +71,24 @@ const handleSubmit = async () => {
   try {
     if (loading.value) return;
     loading.value = true;
-    const data = [];
-
-    tableData.value.forEach((key) => {
-      const obj: Record<string, string> = {};
-
-      for (let i in key) {
-        const prop = mapProp[i];
-        const value = key[i];
-        obj[prop] = value;
-      }
-      data.push(obj);
-    });
 
-    data.forEach((row) => {
-      const source = row.invoiceType.trim();
-      const target = cg_inv_type_list.find(({ label }) => label === source);
-      row.invoiceType = target.value;
-    });
-
-    const buyers = data.map(({ supplierNo }) => supplierNo);
-    const setBuyers = [...new Set(buyers)];
-
-    if (setBuyers.length > 1) {
-      ElMessage.error("卖方公司编码不一致");
-      loading.value = false;
-      return;
-    }
+    const list = [];
 
-    const companyNo = data[0].supplierNo;
+    tableData.value.forEach(item => {
+      list.push({ 
+        ...item, 
+        inv_type: mapLabelToInvtype[item.inv_type],
+        source: mapLabelToSource[item.source], 
+        type: mapLabelToType[item.type],
+        buyer_name: item.inv_buyer_name,
+        buyer_code: item.inv_buyer_code,
+        platform_type: '1',
+        channel: '1',
+        goodType: '1'
+      })
+    })
 
-    if(companyNo != currentCompany.value.companyNo){
-      ElMessage.error("卖方公司编码与当前选中的公司不一致");
-      loading.value = false;
-      return;
-    }
-
-    data.forEach(item => {
-      const isFullyElectionic = item.invoiceType === 'fully_digitalized_special_electronic' || item.invoiceType === 'fully_digitalized_normal_electronic'
-      if(isFullyElectionic){ item.inv_subtotal_amount = item.inv_total }
-      
-      delete item['inv_total']
-      delete item["supplierNo"];
-      delete item["supplierName"];
-      delete item["companyName"];
-      delete item["companyNo"];
-    });
-    const { code, message,data: _d } = await httpBatchAdd({
-      list: data,
-      companyNo,
-      is_comon: '0'
-      // relaComNo: currentCompany.value.companyNo
-    });
+    const { code, message,data: _d } = await httpAdd({ list });
 
     loading.value = false;
 
@@ -266,21 +98,8 @@ const handleSubmit = async () => {
       noMessage: false,
       handler: () => {
         ElMessage.success("数据导入成功!");
-        emit("onSuccess");
+        emit("refresh");
         visible.value = false;
-      },
-       onError() {
-        if (Number(code) === 10004) {
-          ElMessageBox({
-            title: '以下对账单不符合要求',
-            message: h('div', null, [
-              h('p', null, '当前功能仅支持业务类型为采购订单的对账单,以下通用订单类型的对账单不支持该功能:'),
-              h('p', null, _d.join('、')),
-            ])
-          })
-         }else {
-          ElMessage.warning(message)
-         }
       }
     });
   } catch (err) { 
@@ -321,7 +140,7 @@ const cancel = () => {
         :key="sii"
       >
         <template #header>
-          <span v-if="!requireHeaders.includes(mapProp[si.prop]) || si.label === '序号' ">
+          <span v-if="!si.required">
             {{ si.label }}
           </span>
           

+ 76 - 0
src/views/invoiceInOut/provisionOut/components/OrderImportModal/validator.ts

@@ -0,0 +1,76 @@
+import { h } from "vue"
+import { ElMessageBox } from "element-plus"
+import { mapPropertyToLabel, requiredProps } from "./columns-config"
+import { mapLabelToSource, mapLabelToType, sourceOptions, typeOptions } from "../../config/shared"
+import { mapLabelToInvtype, xs_inv_type_list } from "/@/utils/status"
+
+function onDisplayErrorMessage(errors) {
+    ElMessageBox({
+        type: 'warning',
+        title: '数据校验失败',
+        message: h('div', { style: 'display: flex, flex-direction: column' }, {
+            default: () => Object.keys(errors).reduce((prev, index) => {
+                return [
+                    ...prev,
+                    h('p', {}, '第' + index  + '行,' + errors[index])
+                ]
+            }, [])
+        })
+    })
+}
+
+
+export function isValidRequired(results: any[]) {
+    const errors = {}
+
+    let isTypeError = false
+    let isSourceError = false
+    let isInvError = false
+
+    for (const sourceIndex in results) {
+        const item = results[sourceIndex];
+        const index = Number(sourceIndex) + 1;
+
+        for (const key in item) {
+            const value = String(item[key]).trim()
+            if (requiredProps.includes(key) && (!value || !String(value).trim())) {
+                if (errors[index]) {
+                    errors[index] = errors[index] += '、' + mapPropertyToLabel[key]
+                } else {
+                    errors[index] = mapPropertyToLabel[key]
+                }
+            } else if (key === 'type' && !mapLabelToType[value]){
+                isTypeError = true
+            } else if (key === 'source' && !mapLabelToSource[value]) {
+                isSourceError = true
+            } else if (key === 'inv_type' && !mapLabelToInvtype[value]){
+                isInvError = true
+            }
+        }
+
+        if (errors[index]) {
+            errors[index] += '不能为空'
+            if (isTypeError) errors[index] += ' , 类型必须为' + typeOptions.map(({ label }) => label).join('、')
+            if (isSourceError) errors[index] += ' ,订单来源必须为' + sourceOptions.map(({ label }) => label).join('、')
+            if (isInvError) errors[index] += ' ,发票类型必须为' + xs_inv_type_list.map(({ label }) => label).join('、')
+        } else {
+            if (isTypeError) errors[index] = '类型必须为' + typeOptions.map(({ label }) => label).join('、')
+            if (isSourceError) errors[index] = ' 订单来源必须为' + sourceOptions.map(({ label }) => label).join('、')
+            if (isInvError) errors[index] = ' 发票类型必须为' + xs_inv_type_list.map(({ label }) => label).join('、')
+        }
+    }
+
+
+    if (Object.keys(errors).length === 0) {
+        return true
+    } else {
+        onDisplayErrorMessage(errors)
+        return false
+    }
+}
+
+export function isImportDataValid(results: any[]) {
+    /* 校验必填字段是否填入 */
+    if (!isValidRequired(results)) { return false }
+    return true
+}

+ 284 - 19
src/views/invoiceInOut/provisionOut/config/columns.ts

@@ -1,24 +1,289 @@
+import { ElTag } from "element-plus"
+import { cg_order_type_options, xs_inv_type_list } from "/@/utils/status"
+import { h } from "vue"
+import { managerStatusOptions, sourceOptions, statusOptions, typeOptions } from "./shared"
+import { createTooltip } from "/@/utils/tootip"
+
 export const goodColumns = [
-  { label: '处理编号', field: 'TODO', span: 6 },
-  { label: '发票明细ID', field: 'TODO', span: 6 },
-  { label: '业务编号', field:'TODO', span: 6 },
-  { label: '商品编号', field: 'TODO', span: 6 },
-  { label: '商品名称', field: 'TODO', span: 6 },
-  { label: '商品数量', field: 'TODO', span: 6  },
-  { label: '税前单价', field: 'TODO', span: 6 },
-  { label: '税后单价', field: 'TODO', span: 6 },
-  { label: '出库金额', field: 'TODO', span: 6 },
-  
-  { 
-    label: '状态' , 
-    field: 'TODO' 
+  {
+    span: 12,
+    field: 'seller_name',
+    label: '销售方公司'
   },
-
-  { label: '分光', field: 'TODO', span: 6 },
-  { label: '创建人', field: 'TODO', span: 6 },
-  { label: '创建时间', field: 'TODO', span: 6 }
+  {
+    span: 12,
+    field: 'buyer_name',
+    label: '购买方公司'
+  },
+  {
+    span: 12,
+    field: 'goodName',
+    label: '订单商品名称'
+  },
+  {
+    span: 12,
+    field: 'inv_good_name',
+    label: '发票商品名称'
+  },
+  {
+    span: 6,
+    label: '商品类型',
+    render(_, { good_type }) {
+      return h(ElTag, { size: 'small' }, {
+        default: () => cg_order_type_options.find(item => item.value == good_type)?.label || '--'
+      })
+    }
+  },
+  {
+    span: 6,
+    field: 'good_code',
+    label: '商品代码'
+  },
+  {
+    span: 6,
+    field: 'spec',
+    label: '规格'
+  },
+  {
+    span: 6,
+    field: 'unit',
+    label: '单位'
+  },
+  {
+    span: 6,
+    field: 'unit_price',
+    label: '税前单价'
+  },
+  {
+    span: 6,
+    field: 'subunit_price',
+    label: '税后单价'
+  },
+  {
+    span: 6,
+    field: 'unit_weight',
+    label: '重量'
+  },
+  {
+    span: 6,
+    field: 'spectral',
+    label: '分光'
+  },
+  {
+    field: 'cat_name',
+    label: '进项类目',
+    span: 24
+  }
 ]
 
 
-
-export const orderGoodColumns = []
+export const orderGoodColumns = [
+  {
+    field: 'invoiceCode',
+    label: '业务编号',
+    span: 12
+  },
+  {
+    label: '类型',
+    span: 6,
+    render(_, row){
+      return h(ElTag, { type: 'primary', size: 'small' }, {
+        default: () => typeOptions.find(item => item.value == row.type)?.label
+      })
+    }
+  },
+  {
+    label: '数据来源',
+    span: 6,
+    render(_, row) {
+      return h(ElTag, { type: 'primary', size: 'small' }, {
+        default: () => sourceOptions.find(item => item.value == row.source)?.label
+      })
+    }
+  },
+  {
+    label: '状态',
+    span: 6,
+    render(_, row) {
+      return h(ElTag, { type: statusOptions.find(item => item.value == row.status)?.type, size: 'small' }, {
+        default: () => statusOptions.find(item => item.value == row.status)?.label
+      })
+    }
+  },
+  {
+    label: '异常原因',
+    span: 18,
+  },
+  {
+    label: '销售方公司',
+    span: 12,
+    render(_, row){
+      return createTooltip(row.seller_name, "销售方公司纳税识别号 : " + row.seller_code, 340)
+    }
+  },
+  {
+    label: '订单编号',
+    field: 'orderCode',
+    span: 6
+  },
+  {
+    label: '订单主编号',
+    field: 'cxCode',
+    span: 6
+  },
+  {
+    label: '商品名称',
+    span: 12,
+    render(_, row){
+      return createTooltip(row.goodName, "商品编号 : " + row.goodNo, 340)
+    }
+  },
+  {
+    label: '单位',
+    field: 'unit',
+    span: 6
+  },
+  {
+    label: '商品数量',
+    field: 'num',
+    span: 6
+  },
+  {
+    label: '商品单价',
+    field: 'goodPrice',
+    span: 6
+  },
+  {
+    label: '订单总金额',
+    field: 'totalPrice',
+    span: 6
+  },
+  {
+    label: '税目',
+    field: 'cat_code',
+    span: 6
+  },
+  {
+    label: '税率',
+    span: 6,
+    render(_, row){
+      return (Number(row.tax) * 100) + '%'
+    }
+  },
+  {
+    label: '购买方公司',
+    span: 12,
+    render(_, row){
+      return createTooltip(row.buyer_name, "购买方公司纳税识别号 : " + row.buyer_code, 340)
+    }
+  },
+  {
+    label: '关联金额',
+    field: 'inv_fee',
+    span: 6
+  },
+  {
+    label: '发票号码',
+    field: 'inv_number',
+    span: 6
+  },
+  {
+    label: '发票销售方公司',
+    span: 12,
+    render(_, row){
+      return createTooltip(row.inv_seller_name, "发票销售方公司编号 : " + row.inv_seller_code, 340)
+    }
+  },
+  {
+    label: '发票明细ID',
+    span: 6,
+    field: 'inv_item_id'
+  },
+  {
+    label: '发票类型',
+    span: 6,
+    render(_, row){
+      return xs_inv_type_list.find(item => item.value == row.inv_type)?.label
+    }
+  },
+  {
+    label: '货物或应税劳务、服务名称',
+    span: 12,
+    field: 'inv_good_name'
+  },
+  {
+    label: '规格型号',
+    span: 6,
+    field: 'inv_spec'
+  },
+  {
+    label: '发票商品单位',
+    span: 6,
+    field: 'inv_unit'
+  },
+  {
+    label: '发票商品数量',
+    span: 6,
+    field: 'inv_num'
+  },
+  {
+    label: '税前单价',
+    span: 6,
+    field: 'inv_subprice'
+  },
+  {
+    label: '税前总价',
+    span: 6,
+    field: 'inv_subtotal'
+  },
+  {
+    label: '发票税率',
+    field: 'inv_tax',
+    span: 6
+  },
+  {
+    label: '发票税额',
+    field: 'inv_tax_total',
+    span: 6
+  },
+  {
+    label: '税后单价',
+    field: 'inv_price',
+    span: 6
+  },
+  {
+    label: '税后单价',
+    field: 'inv_total',
+    span: 6
+  },
+  {
+    label: '类目编号状态',
+    span: 6,
+    render(_, row){
+      return h(ElTag, { size: 'small', type: row.cat_diff == '0' ? 'danger' : 'success' }, {
+        default: () => row.cat_diff == '0' ? '不一致' : '一致'
+      })
+    }
+  },
+  {
+    label: '税率状态',
+    span: 6,
+    render(_, row) {
+      return h(ElTag, { size: 'small', type: row.tax_diff == '0' ? 'danger' : 'success' }, {
+        default: () => row.tax_diff == '0' ? '不一致' : '一致'
+      })
+    }
+  },
+  {
+    label: '业务类型',
+    span: 6,
+    render(_, row) {
+      return managerStatusOptions.find((item) => item.value == row.manager_status)?.label
+    }
+  },
+  {
+    label: '备注',
+    span: 12,
+    field: 'remark'
+  }
+]

+ 268 - 34
src/views/invoiceInOut/provisionOut/config/content.config.ts

@@ -1,29 +1,96 @@
 import { ContentConfig } from "/@/components/PageContent";
-import { httpList } from "/@/api/invoiceInOut/provisionOut";
-import { renderIconLabelLeft } from "/@/utils/columnRenderHelper";
-import { statusOptions } from "./shared"
-import { ElTag } from "element-plus";
+import { httpList } from "/@/api/invoiceInOut/inOutManager";
 import { h } from "vue";
+import { ElTag } from "element-plus";
+import { typeOptions, sourceOptions, statusOptions, managerStatusOptions } from "./shared";
+import { xs_inv_type_list } from "/@/utils/status";
 
 const columns = [
   { 
-    label: "发票明细ID", 
-    prop: "inv_item_id", 
-    width: 90 
+    label: "业务编号", 
+    prop: "invoiceCode", 
+    width: 150 
   },
-  
+
   { 
-    label: '业务编号', 
-    minWidth: 150,
-    prop: 'invoiceCode' 
+    label: "类型", 
+    width: 80,
+    cellRenderer({ row }){
+      return h(ElTag, { type: 'primary', size: 'small' }, {
+        default: () => typeOptions.find(item => item.value == row.type)?.label
+      })
+    } 
   },
+
   { 
-    label: '商品名称', 
-    prop: 'goodName', 
-    minWidth: 140,
-    ...renderIconLabelLeft('goodNo', 'goodName', '商品编号:')
+    label: '数据来源', 
+    width: 90,
+    cellRenderer({ row }){
+      return h(ElTag, { type: 'primary', size: 'small' }, {
+        default: () => sourceOptions.find(item => item.value == row.source)?.label
+      })
+    } 
+  },
+
+
+  { 
+    label: "状态", 
+    width: 90,
+    cellRenderer({ row }) {
+      return h(
+        ElTag, 
+          { type: statusOptions.find(item => item.value == row.status)?.type, size: 'small' }, 
+          { default: () => statusOptions.find(item => item.value == row.status)?.label }
+      )
+    } 
+  },
+  { 
+    label: '异常原因', 
+    width: 120,
+    prop: 'error_remark'
+  },
+  { 
+    label: '销售方公司纳税识别号', 
+    prop: 'seller_code', 
+    width: 150 
+  },
+
+  { 
+    label: '销售方公司', 
+    prop: 'seller_name', 
+    width: 150 
+  },
+
+  { 
+    label: '订单编号', 
+    prop: 'orderCode', 
+    width: 160 
   },
   
+  { 
+    label: "订单主编号", 
+    prop: "cxCode", 
+    width: 160 
+  },
+  
+  { 
+    label: "商品编号", 
+    prop: "goodNo", 
+    width: 120 
+  },
+
+  { 
+    label: "商品名称", 
+    prop: "goodName", 
+    width: 120 
+  },
+
+  { 
+    label: "单位", 
+    prop: "unit", 
+    width: 60 
+  },
+
   { 
     label: '商品数量', 
     prop: 'num', 
@@ -31,44 +98,211 @@ const columns = [
   },
   
   { 
-    label: '税前单价', 
-    prop: 'inv_subprice', 
+    label: '商品单价', 
+    prop: 'goodPrice', 
+    width: 100 
+  },
+  
+  { 
+    label: '订单总金额', 
+    prop: 'totalPrice', 
     width: 120 
   },
 
   { 
-    label: '税后单价', 
-    prop: 'inv_price', 
+    label: '税', 
+    prop: 'cat_code', 
     width: 120 
   },
+  
+  { 
+    label: '税率', 
+    width: 60,
+    cellRenderer({ row }){
+      return (row.tax * 100) + '%'
+    } 
+  },
 
   { 
-    label: "状态", 
-    width: 80,
-    cellRenderer({ row }){ 
-      return h(ElTag, { size: 'small', type: statusOptions.find(item => row.status == item.value)?.type }, {
-        default: () => statusOptions.find(item => row.status == item.value)?.label
-      }) 
-    }
+    label: '关联金额', 
+    prop: 'inv_fee', 
+    width: 120 
   },
-  
+
   { 
-    label: "创建人", 
-    prop: "apply_name", 
+    label: '购买方公司名称', 
+    prop: 'buyer_name',
     width: 120 
   },
   
   { 
-    label: "创建时间", 
-    prop: "create_time", 
+    label: '购买方公司纳税号', 
+    prop: 'buyer_code', 
     width: 120 
   },
   
+  { 
+    label: '发票号码', 
+    prop: 'inv_number', 
+    width: 100 
+  },
+  
+  { 
+    label: '发票类型', 
+    width: 180,
+    cellRenderer({ row }){
+      return xs_inv_type_list.find((item) => item.value === row.inv_type)?.label
+    }
+  },
+
+  
+  { 
+    label: '销售方公司', 
+    prop: 'inv_seller_code', 
+    width: 180 
+  },
+  
+  { 
+    label: '销售方公司纳税号', 
+    prop: 'inv_seller_name', 
+    width: 180 
+  },
+
+  { 
+    label: '发票明细ID', 
+    prop: 'inv_item_id', 
+    width: 100 
+  },
+
+  { 
+    label: '货物或应税劳务、服务名称', 
+    prop: 'inv_good_name', 
+    width: 170 
+  },
+
+  { 
+    label: '类目编号', 
+    prop: 'cat_code', 
+    width: 100 
+  },
+  
+  { 
+    label: '规格型号', 
+    prop: 'inv_spec', 
+    width: 80 
+  },
+  
+  { 
+    label: '发票商品单位', 
+    prop: 'inv_unit', 
+    width: 100 
+  },
+  
+  { 
+    label: '发票商品数量', 
+    prop: 'inv_num', 
+    width: 100 
+  },
+
+  { 
+    label: '税前单价', 
+    prop: 'inv_subprice', 
+    width: 100 
+  },
+  
+  { 
+    label: '税前总价', 
+    prop: 'inv_subtotal', 
+    width: 100 
+  },
+  
+  { 
+    label: '发票税率', 
+    prop: 'inv_tax', 
+    width: 80 
+  },
+  
+  { 
+    label: '发票税额', 
+    prop: 'inv_tax_total', 
+    width: 80 
+  },
+  
+  { 
+    label: '税后单价', 
+    prop: 'inv_price', 
+    width: 100 
+  },
+  
+  { 
+    label: '税后总额', 
+    prop: 'inv_total', 
+    width: 100 
+  },
+  
+  { 
+    label: '类目编号状态', 
+    width: 100,
+    cellRenderer({ row }){
+      return h(ElTag, { size: 'small', type: row.cat_diff == '0' ? 'danger' : 'success' }, {
+        default: () => row.cat_diff == '0' ? '不一致' : '一致'
+      })
+    }
+  },
+
+  { 
+    label: '税率状态', 
+    prop: 'TODO', 
+    width: 100,
+    cellRenderer({ row }) {
+      return h(
+        ElTag, 
+        { size: 'small', type: row.tax_diff == '0' ? 'danger' : 'success' }, 
+        { default: () => row.tax_diff == '0' ? '不一致' : '一致' }
+      )
+    }
+  },
+  
+  { 
+    label: '备注', 
+    prop: 'remark', 
+    width: 100 
+  },
+
+  { 
+    label: '业务类型', 
+    width: 80,
+    cellRenderer({ row }){
+      return managerStatusOptions.find((item) => item.value == row.manager_status)?.label
+    }
+  },
+  { 
+    label: '关联商品ID', 
+    prop: 'goodNo', 
+    width: 80,
+    cellRenderer({ row }){
+      if (row.ProductRela.length === 0){
+        return null
+      }
+      return row.ProductRela[0].id
+    }
+  },
+  { 
+    label: '关联商品名称', 
+    prop: 'goodName', 
+    width: 100,
+    cellRenderer({ row }) {
+      if (row.ProductRela.length === 0) {
+        return null
+      }
+      return row.ProductRela[0].goodName
+    }
+  },
   { 
     label: "操作", 
     fixed: "right", 
-    slot: "operation",
-    width: 45
+    width: 50, 
+    slot: "operation" 
   }
 ];
 
@@ -77,7 +311,7 @@ const contentConfig: ContentConfig = {
   title: "商品管理",
   permissions: ['007'],
   apis: { 
-    httpList: (parameter = {}) => httpList({ ...parameter, manager_status: '2' }),
+    httpList: (parameter = {}) => httpList({ ...parameter, channel: [1, 2], manager_status: '3' }),
     httpAdd: true
   }
 };

+ 1 - 27
src/views/invoiceInOut/provisionOut/config/search.config.ts

@@ -1,33 +1,7 @@
 import { FormConfig } from "/@/components/PageSearch";
-import { goodTypeOptions } from "/@/utils/status";
 
 const searchFormConfig: FormConfig = {
-  formItems: [
-    {
-      field: 'goodNo',
-      type: 'input',
-      placeholder: '商品编码'
-    },
-    {
-      field: 'goodType',
-      type: 'select',
-      options: goodTypeOptions,
-      placeholder: '商品类型'
-    },
-    {
-      field: "timer",
-      type: "date_picker",
-      span: 7,
-      label: '创建时间',
-      otherOptions: {
-        type: "daterange",
-        startProp: "start",
-        endProp: "end",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间"
-      }
-    },
-  ]
+  formItems: []
 };
 
 export default searchFormConfig;

+ 33 - 0
src/views/invoiceInOut/provisionOut/config/shared.ts

@@ -1,4 +1,37 @@
+export const typeOptions = [
+    { value: '1', label: '入库' },
+    { value: '2', label: '出库' },
+    { value: '3', label: '入库红冲' },
+    { value: '4', label: '出库红冲' },
+]
+
+export const mapLabelToType = typeOptions.reduce((prev, current) => ({
+    ...prev,
+    [current.label]: current.value
+}), {})
+
+
+
+export const sourceOptions = [
+    { value: '1', label: '采销结算' },
+    { value: '2', label: '线下订单' }
+]
+
+export const mapLabelToSource = sourceOptions.reduce((prev, current) => ({
+    ...prev,
+    [current.label]: current.value
+}), {})
+
+
+
 export const statusOptions = [
     { value: '1', label: '待处理', type: 'warning' },
     { value: '2', label: '处理完成', type: 'success' }
+]
+
+
+export const managerStatusOptions = [
+  { value: '1', label: '正常' },
+  { value: '2', label: '计提' },
+  { value: '3', label: '异常' }
 ]

+ 224 - 13
src/views/invoiceInOut/provisionOut/detail.vue

@@ -1,38 +1,249 @@
 <script setup lang="ts">
-import { useDetail } from "/@/hooks/core/useDetail"
-import BaseForm from "./components/BaseForm/index.vue"
+import { computed, reactive, ref } from "vue";
 
+import { orderGoodColumns } from "./config/columns"
+import { useDetail } from "/@/hooks/core/useDetail";
+import BasicDescriptions from "/@/components/BasicDescriptions";
+import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
+import ChooseGoodModal from "./components/ChooseGoodModal/index.vue"
+import ChildGoodEditModal from "./components/ChildGoodEditModal.vue";
 
-import { 
-  ElCollapseItem, 
-  ElCollapse, 
-  ElTabPane, 
-  ElTabs 
-} from "element-plus"
+import { httpDetail, httpStatus } from "/@/api/invoiceInOut/inOutManager";
+import { ElForm, ElMessage } from "element-plus";
+import { useTask } from "/@/hooks/core";
 
+import SealMonthPicker from "/@/components/SealMonthPicker/index.vue"
 
-const { id, isDetail, title, collapses } = useDetail({ baseName: '计提明细处理', collapseLen: 2 });
 
+const { title, collapses, id } = useDetail({ baseName: '出入库', collapseLen: 2 });
+
+const visible = ref(false)
+
+const detailTask = useTask()
+
+const state = reactive({
+  editVisible: false,
+  index: -1,
+  data: {}
+})
+
+const loading = ref(false)
+const formRef = ref<InstanceType<typeof ElForm> | null>(null)
+
+const searchParameter = computed(() => {
+  // 入库、入库红冲和出库红冲 只能选择真实成本的商品,出库类型可以选择预估成本商品
+  if(!detailTask.data) return {}
+  return detailTask.data.type == '2' ? { basic_status: '1'  } : { basic_status: '2' }
+})
+
+const formData = ref({ goods: [], month: '' })
 
 const rules = {
-  isToAdjust: [{ required: true, message: '请选择是否添加至调整单', trigger: 'change' }],
-  amount: [{ required: true, message: '请输入调整金额', trigger: 'change' }]
+  goods: [{ required: true, message: '请选择操作商品' }],
+  month: [{ required: true, message: '请选择入账月份' }]
+}
+
+const disabledTomorrow = (time) => {
+  const now = new Date();
+  return time.getTime() > now.setHours(0, 0, 0, 0);
+};
+
+function handleGoodComfirm(values: any[]){
+  const ids = formData.value.goods.map(({ id }) => id)
+  const list = []
+
+  values.forEach(item => {
+    if(!ids.includes(item.id)){ list.push(item) }
+  })
+
+  formData.value.goods = [...list,  ...formData.value.goods]
 }
 
+function handleChildGoodEdit(index: number){
+  state.index = index
+  state.editVisible = true
+  state.data = { ...formData.value.goods[index] }
+}
+
+function getSpanNumber(data, prop) {
+  let length = Array.isArray(data) ? data.length : 0;
+  if (length > 0) {
+    let position = 0;
+    let temp = data[0][prop];
+    let result = [1];
+    for (let i = 1; i < length; i++) {
+      if (data[i][prop] == temp) {
+        result[position] += 1;
+        result[i] = 0;
+      } else {
+        position = i;
+        result[i] = 1;
+        temp = data[i][prop];
+      }
+    }
+    return result;
+  } else {
+    return [0];
+  }
+}
+
+function spanMethod({ row:_1, column:_2, rowIndex, columnIndex }: any){
+  if ([0, 1, 2, 7].includes(Number(columnIndex))){
+      let nameSpan = getSpanNumber(formData.value.goods, "id");
+      return { rowspan: nameSpan[rowIndex], colspan: 1 };
+    }
+}
+
+async function onSubmit(){
+  try{
+    await formRef.value.validate()
+    const relaArrCopy = formData.value.goods.map(({ id, goodNum }) => ({ id, num: goodNum }))
+
+    const relaArr = []
+    const mapId = {}
+    
+    for(const item of relaArrCopy){
+      if (!mapId[item.id]){
+        relaArr.push(item)
+        mapId[item.id] = true
+      } 
+    }
 
+    loading.value = true
+    const { code, message } = await httpStatus({ relaArr, manager_id: id.value })
+    loading.value = false
 
+    if (code == 0) {
+      ElMessage.success('操作完成')
+      detailTask.run(httpDetail({ id: id.value }))
+    } else {
+      ElMessage.warning(message)
+    }
+  }catch(err){
+    console.log(err)
+  }
+}
+
+function handleEditGood(index, data){
+  const { id } = formData.value.goods[index]
+  formData.value.goods.forEach((item, index) => {
+    if(item.id === id){
+      formData.value.goods[index].goodNum = data.goodNum
+
+      if(formData.value.goods[index].childNum){ formData.value.goods[index].childActionNum = formData.value.goods[index].childNum * data.goodNum }
+    }
+  })
+}
+
+function handleDelete(index){
+  const { id } = formData.value.goods[index]
+  formData.value.goods = formData.value.goods.filter((item) => item.id !== id)
+}
+
+id.value && detailTask.run(httpDetail({ id: id.value }))
 </script>
 
 <template>
-  <div class="padding__container">
+  <div class="padding__container" v-loading="detailTask.loading">
     <ElTabs>
       <ElTabPane :label="title">
         <ElCollapse v-model="collapses">
           <ElCollapseItem :title="title" name="1">
-              <BaseForm />
+            <BasicDescriptions
+              :columns="orderGoodColumns" 
+              :data="detailTask.data" 
+              v-if="detailTask.data"
+            />
+          </ElCollapseItem>
+
+          <!-- && detailTask.data.type == '1' -->
+          <ElCollapseItem 
+            title="出入库信息确认" 
+            name="2" 
+            v-if="detailTask.data && detailTask.data.status == '1'"
+          >
+            <ElForm ref="formRef" :rules="rules" :model="formData">
+              <ElFormItem label="操作商品" prop="goods">
+                <ElTable
+                :span-method="spanMethod"
+                  border 
+                  size="small" 
+                  :data="formData.goods"
+                >
+                  <ElTableColumn label="商品编号" prop="skuCode" show-overflow-tooltip />
+                  <ElTableColumn label="商品名称" prop="goodName" show-overflow-tooltip />
+                  <ElTableColumn label="操作数量" prop="goodNum" show-overflow-tooltip/>
+                  <ElTableColumn label="子商品编号" prop="childCode" show-overflow-tooltip />
+                  <ElTableColumn label="销售方公司纳税识别号" width="140px" prop="seller_code" show-overflow-tooltip />
+                  <ElTableColumn label="子商品比例" prop="childNum" show-overflow-tooltip />
+                  <ElTableColumn label="子商品操作数量" prop="childActionNum" show-overflow-tooltip />
+
+                  <ElTableColumn width="70px" label="操作">
+                    <template #header>
+                      <div class="w-full flex justify-between">
+                        <span>操作</span>
+                        <ElTooltip content="添加" placement="top">
+                          <ElButton
+                            link
+                            size="small" 
+                            type="primary" 
+                            :icon="useRenderIcon('add')"
+                            @click="visible = true"
+                          />
+                        </ElTooltip>
+                      </div>
+                    </template>
+
+                    <template #="{ $index }">
+                      <ElTooltip content="编辑" placement="top">
+                        <ElButton 
+                          :icon="useRenderIcon('edits')"
+                          type="primary"
+                          size="small"
+                          link
+                           @click="handleChildGoodEdit($index)"
+                        />
+                      </ElTooltip>
+
+
+                      <ElTooltip content="删除" placement="top">
+                        <ElButton 
+                          :icon="useRenderIcon('delete')"
+                          type="danger"
+                          size="small"
+                          link
+                          @click="handleDelete($index)"
+                        />
+                      </ElTooltip>
+                    </template>
+                  </ElTableColumn>
+                </ElTable>
+              </ElFormItem>
+
+              <ElFormItem label="入账月份" prop="month">
+                  <SealMonthPicker  placeholder="入账月份" :company_code="detailTask.data.buyer_code"  />
+              </ElFormItem>
+            </ElForm>
+
+            <div class="flex w-full justify-end">
+              <ElButton type="primary" @click="onSubmit">保存</ElButton>
+            </div>
           </ElCollapseItem>
         </ElCollapse>
       </ElTabPane>
     </ElTabs>
+
+    <ChooseGoodModal 
+      v-model:visible="visible"
+      :otherSearchParameter="searchParameter"
+      @submit="handleGoodComfirm"
+    />
+
+    <ChildGoodEditModal 
+      :data="state.data"
+      :index="state.index"
+      v-model:visible="state.editVisible"
+      @submit="handleEditGood"
+    />
   </div>
 </template>

+ 77 - 11
src/views/invoiceInOut/provisionOut/index.vue

@@ -1,28 +1,40 @@
 <script setup lang="ts">
-import { reactive } from "vue";
+import { reactive, ref } from "vue";
 import { useRouter } from "vue-router";
 import { usePageSearch, type PageHooks, type PageEvents } from "/@/hooks/page";
+import NotOrderImportModal from "./components/NotOrderImportModal/index.vue"
+import OrderImportModal from "./components/OrderImportModal/index.vue"
+import { notOrderTemplate } from "./components/NotOrderImportModal/columns-config"
+import { orderTemplate } from "./components/OrderImportModal/columns-config"
+import { useRenderIcon } from "/@/components/ReIcon/src/hooks"
 import contentConfig from "./config/content.config";
 import searchConfig from "./config/search.config";
+import { utils, writeFile } from "xlsx";
 
-const PageName = "productManager";
+
+const PageName = "inOutManager";
 const router = useRouter();
 
-const hooks: PageHooks = {
-  pageSearchHook: () => usePageSearch(undefined, undefined, searchConfig)
-};
+const state = reactive({ orderImportVisible: false, notOrderImportVisible: false })
+const hooks: PageHooks = { pageSearchHook: () => usePageSearch(undefined, undefined, searchConfig) };
 
-const state = reactive({ 
-  orderImportVisible: false, 
-  notOrderImportVisible: false 
-})
+const pageContentRef = ref(null)
 
 const events: PageEvents = {
   content: {
-    create: () => router.push('/invoiceInOut/provisionOutDetail'),
-    preview: ({ id }) => router.push("/invoiceInOut/provisionOutDetail?id=" + id)
+    create: () => router.push('/invoiceInOut/productManagerDetail'),
+    preview: ({ id }) => router.push("/invoiceInOut/inOutManagerDetail?id=" + id)
   }
 };
+
+function onDownload(type = 'notOrder'){
+  const data = type === 'notOrder' ? notOrderTemplate : orderTemplate
+  const title = type === 'notOrder' ? '非订单商品模板.xlsx' : '订单商品模板.xlsx'
+  const workBook = utils.book_new();
+  const workSheet = utils.json_to_sheet(data);
+  utils.book_append_sheet(workBook, workSheet, "sheet");
+  writeFile(workBook, title, { bookType: "xlsx" });
+}
 </script>
 
 <template>
@@ -32,6 +44,60 @@ const events: PageEvents = {
       :events="events"
       :searchConfig="searchConfig"
       :contentConfig="contentConfig"
+      :get-content-ref="ref => pageContentRef = ref"
+    >
+      <template #content_header>
+        <ElDropdown>
+          <ElButton 
+            size="small" 
+            type="primary"
+            style="margin-right: 10px;margin-top:1px"
+            :icon="useRenderIcon('arrow-up-line')"
+          >商品导入
+          </ElButton>
+          
+          <template #dropdown>
+            <ElDropdownMenu>
+              <ElDropdownItem @click="state.orderImportVisible = true">订单商品</ElDropdownItem>
+              <ElDropdownItem @click="state.notOrderImportVisible = true">非订单商品</ElDropdownItem>
+            </ElDropdownMenu>
+          </template>
+        </ElDropdown>
+
+        <ElDropdown>
+          <ElButton 
+            size="small" 
+            style="margin-right: 10px;margin-top:1px"
+            :icon="useRenderIcon('arrow-down-line')"
+          >商品导入模板下载</ElButton>
+          
+          <template #dropdown>
+            <ElDropdownMenu>
+              <ElDropdownItem @click="onDownload('order')">订单商品</ElDropdownItem>
+              <ElDropdownItem @click="onDownload('notOrder')">非订单商品</ElDropdownItem>
+            </ElDropdownMenu>
+          </template>
+        </ElDropdown>
+      </template>
+      <!-- <template #content_action>
+        <ElTooltip content="取消操作" placement="top">
+          <ElButton 
+            :icon="useRenderIcon('delete')"
+            type="danger"
+            link
+          />
+        </ElTooltip>
+      </template> -->
+    </PageContainer>
+
+    <OrderImportModal 
+      v-model:visible="state.orderImportVisible"
+      @refresh="pageContentRef?.onSearch()"
+    />
+
+    <NotOrderImportModal 
+      v-model:visible="state.notOrderImportVisible"
+      @refresh="pageContentRef?.onSearch()"
     />
   </PageAuth>
 </template>

+ 0 - 0
src/views/invoiceInOut/provisionOut/计提出库明细处理 → src/views/invoiceInOut/provisionOut/出入库管理