(function(){ const navAnchor=document.querySelector('[data-s="ai"]'); if(navAnchor){navAnchor.insertAdjacentHTML('beforebegin',``);} const baseNav=document.querySelector('#group-base'); if(baseNav&&!baseNav.querySelector('[data-s="erp-kingdee-basic"]'))baseNav.insertAdjacentHTML('beforeend',''); const salesSection=document.getElementById('sales-outbound-standard'); if(salesSection){ const description=salesSection.querySelector('.section-title p');if(description)description.innerText='查看实际参与收入核对的标准数据。Excel导入正式数据仅指以前导入的金蝶销售出库单,不包含其他Excel导入。'; const sourceFilter=document.getElementById('sobSourceFilter');if(sourceFilter){const manual=[...sourceFilter.options].find(option=>option.value==='kingdee');if(manual)manual.text='Excel导入正式数据';if(![...sourceFilter.options].some(option=>option.value==='kingdee_api'))sourceFilter.add(new Option('金蝶API','kingdee_api'),manual?manual.index:-1);} const kingdeeHead=document.getElementById('sobKt')?.closest('table')?.querySelector('thead tr');if(kingdeeHead)kingdeeHead.innerHTML='数据来源销售组织正式状态期间平台订单号/关联单据物料编码仓库客户币别数量金额'; } document.querySelector('main').insertAdjacentHTML('beforeend',`

基础资料维护

维护金蝶销售出库单可选择的销售组织。

勾选“参与收入核对”的启用组织会在发布时自动合并;任一组织缺少当前期间成功批次时禁止发布。
销售组织编码销售组织名称状态收入核对操作

积加API配置

配置积加开放平台请求地址、应用凭证及两个报表接口。

当前阶段:接口数据仅保存到原始预览批次,不进入销售出库数据和收入核对。

配送报告

自发货订单

金蝶云星空API配置

统一维护第三方登录信息,并分别配置各类金蝶单据查询接口。

配置说明:API地址、账套ID、用户名、App ID、App Secret、语言和超时由所有单据共用;每种单据只维护自己的 FormId、组织字段、日期字段和查询字段。

统一登录配置

单据接口配置

销售出库、其他入库、其他出库、调拨、应收等单据分别维护,后续可继续新增。

顺序配置编码单据名称FormId组织字段日期字段固定过滤条件状态操作
${erpApiPullSection('erp-kingdee-sales','金蝶销售出库单','kingdee_sales_outbound',true)} ${erpApiPullSection('erp-kingdee-other-outbound','金蝶其他出库单','kingdee_other_outbound',true)} ${erpApiPullSection('erp-kingdee-other-inbound','金蝶其他入库单','kingdee_other_inbound',true)} ${erpApiPullSection('erp-kingdee-transfer','金蝶调拨单','kingdee_transfer',true)} ${erpApiPullSection('erp-kingdee-accounts-receivable','金蝶费用应收单','kingdee_accounts_receivable',true)} ${erpApiPullSection('erp-jiaplus-delivery','积加配送报告','jiaplus_delivery',false)} ${erpApiPullSection('erp-jiaplus-self-ship','积加自发货订单','jiaplus_self_ship',false)} `); function erpApiNavAllowed(resource){return typeof hasResource!=='function'||hasResource(resource);} window.applyErpApiPermissions=function(){ document.querySelectorAll('#group-erp-api [data-resource]').forEach(el=>{el.style.display=erpApiNavAllowed(el.dataset.resource)?'':'none';}); ['kingdee','jiaplus'].forEach(group=>{const wrap=document.getElementById('group-erp-api-'+group),groupTitle=document.getElementById('group-erp-api-'+group+'-title'),visible=wrap&&[...wrap.querySelectorAll('[data-resource]')].some(el=>el.style.display!=='none');if(groupTitle)groupTitle.style.display=visible?'':'none';}); const visible=[...document.querySelectorAll('#group-erp-api [data-resource]')].some(el=>el.style.display!=='none'); const title=document.getElementById('group-erp-api-title');if(title)title.style.display=visible?'':'none'; }; window.applyErpBasicPermissions=function(){const el=document.querySelector('#group-base [data-resource="erp.base.kingdee_basic"]');if(el)el.style.display=erpApiNavAllowed('erp.base.kingdee_basic')?'':'none';}; const oldApply=window.applyPermissions; if(oldApply)window.applyPermissions=function(){oldApply();window.applyErpApiPermissions();window.applyErpBasicPermissions();}; const meta={ 'erp-kingdee-basic':['基础资料维护','维护金蝶销售出库单可选择的销售组织编码、名称和状态。'], 'erp-jiaplus-config':['积加API配置','配置积加开放平台连接与原始报表接口。'], 'erp-kingdee-config':['金蝶云星空API配置','统一维护登录信息,分别配置各类金蝶单据查询接口。'], 'erp-kingdee-sales':['金蝶销售出库单','按销售组织和出库期间拉取原始数据进行核验。'], 'erp-kingdee-other-outbound':['金蝶其他出库单','按年度、期间和组织拉取,发布后用于库存核对。'], 'erp-kingdee-other-inbound':['金蝶其他入库单','按年度、期间和组织拉取,发布后用于库存核对。'], 'erp-kingdee-transfer':['金蝶调拨单','按年度、期间和组织拉取,发布后用于库存核对。'], 'erp-kingdee-accounts-receivable':['金蝶费用应收单','按年度、期间和组织拉取预览;费用核对逻辑暂未启用。'], 'erp-jiaplus-delivery':['积加配送报告','按期间拉取积加配送报告原始数据进行核验。'], 'erp-jiaplus-self-ship':['积加自发货订单','按期间拉取积加自发货订单原始数据进行核验。'] }; document.querySelectorAll('#group-erp-api [data-s]').forEach(el=>el.addEventListener('click',function(ev){ ev.stopPropagation();document.querySelectorAll('.nav').forEach(n=>n.classList.remove('active'));this.classList.add('active'); document.querySelectorAll('.section').forEach(s=>s.classList.remove('active'));const id=this.dataset.s;document.getElementById(id).classList.add('active'); title.innerText=meta[id][0];desc.innerText=meta[id][1]; if(id==='erp-jiaplus-config')loadErpJiaplusConfig();else if(id==='erp-kingdee-config')loadErpKingdeeConfig();else if(id==='erp-kingdee-basic')loadKingdeeSalesOrgs();else loadErpApiPreview(this.dataset.source||erpApiSourceBySection(id)); })); document.querySelectorAll('#group-base [data-s="erp-kingdee-basic"]').forEach(el=>el.addEventListener('click',function(ev){ev.stopPropagation();document.querySelectorAll('.nav').forEach(n=>n.classList.remove('active'));this.classList.add('active');document.querySelectorAll('.section').forEach(s=>s.classList.remove('active'));document.getElementById('erp-kingdee-basic').classList.add('active');title.innerText=meta['erp-kingdee-basic'][0];desc.innerText=meta['erp-kingdee-basic'][1];loadKingdeeSalesOrgs();})); })(); window.renderSalesOutboundStandard=async function(){ try{ const filters={doc_type:'sales_outbound',source_type:sobSourceFilter.value,year:sobYearFilter.value,month:sobMonthFilter.value,sku:sobSkuFilter.value,warehouse:sobWhFilter.value,reconcile_filter:sobReconFilter.value,page_size:st.sobPage.size}; const jData=await apiGet('/api/data/standard?side=j&page='+st.sobPage.j+'&'+toQuery(filters)); const kData=await apiGet('/api/data/standard?side=k&page='+st.sobPage.k+'&'+toQuery(filters)); sobJPageInfo.innerText='第 '+jData.page+' / '+Math.max(1,Math.ceil(jData.total/jData.page_size))+' 页,共 '+jData.total+' 条'; sobKPageInfo.innerText='第 '+kData.page+' / '+Math.max(1,Math.ceil(kData.total/kData.page_size))+' 页,共 '+kData.total+' 条'; sobJt.innerHTML=jData.rows.map(r=>{let skipTag=r.skip_reconcile?' 不核对':'';return `${r.source_name}${skipTag}${r.period}${r.order_no||r.bill_no||''}${r.sku}${r.warehouse}${r.store||''}${r.currency||''}${f(r.qty)}${money(r.amount)}`;}).join('')||'
暂无积加数据
'; sobKt.innerHTML=kData.rows.map(r=>{let skipTag=r.customer_skip?' 不核对':'';let confirmCell='';if(r.skip_reconcile){confirmCell=` 需手工确认 `;}const isApi=r.source_type==='kingdee_api',sourceLabel=isApi?'金蝶API':'Excel导入正式数据',activeTag=r.active_source?'当前正式':'备用';return `${sourceLabel}${skipTag}${confirmCell}${r.sales_org||'-'}${activeTag}${r.period}${r.order_no||r.bill_no||''}${r.sku}${r.warehouse}${r.store||''}${r.currency||''}${f(r.qty)}${money(r.amount)}`;}).join('')||'
暂无金蝶数据
'; }catch(error){console.error(error);toast('加载销售出库数据失败:'+error.message);} }; function erpApiPullSection(id,label,source,showOrg){const isSales=source==='kingdee_sales_outbound',isInventory=['kingdee_other_outbound','kingdee_other_inbound','kingdee_transfer'].includes(source),isReceivable=source==='kingdee_accounts_receivable';let actions;if(isSales)actions='';else if(isInventory)actions=``;else if(isReceivable)actions=``;else actions=``;const description=isSales?'按年度、期间和销售组织拉取;参与收入核对的组织合并发布。':isInventory?'按年度、期间和组织拉取;发布时替换该期间原有金蝶Excel数据。':isReceivable?'按年度、期间和组织拉取预览;费用核对逻辑暂未启用。':'按所选时间范围拉取完整原始字段,确认无误后发布到销售出库数据与收入核对。';const notice=isSales?'拉取数据先保存在预览区,点击“发布到收入核对”后才成为正式数据。':isInventory?'拉取数据先保存在预览区;发布后按期间覆盖对应类型的金蝶Excel/API正式数据,不与历史数据叠加。':isReceivable?'当前仅支持拉取、查询和导出,不会写入库存核对或收入核对。':'拉取数据先保存在预览区,发布后写入销售出库数据的积加页签;重复业务明细会覆盖。';return `

${label}

${description}

${actions}
预览隔离:${notice}
${showOrg?`
`:`
`}
后台任务:尚未拉取
每页
`;} const erpApiSources=['kingdee_sales_outbound','kingdee_other_outbound','kingdee_other_inbound','kingdee_transfer','kingdee_accounts_receivable','jiaplus_delivery','jiaplus_self_ship']; const erpApiPages=Object.fromEntries(erpApiSources.map(source=>[source,1])); const erpApiPageSizes=Object.fromEntries(erpApiSources.map(source=>[source,100])); const erpApiTotals=Object.fromEntries(erpApiSources.map(source=>[source,0])); const erpApiBatchCache=Object.fromEntries(erpApiSources.map(source=>[source,[]])); const erpApiLastJobStatus=Object.fromEntries(erpApiSources.map(source=>[source,''])); const erpJiaplusFilterTimers={}; function applyJiaplusPreviewFilter(source){erpApiPages[source]=1;renderErpApiRecords(source);} function scheduleJiaplusPreviewFilter(source){clearTimeout(erpJiaplusFilterTimers[source]);erpJiaplusFilterTimers[source]=setTimeout(()=>applyJiaplusPreviewFilter(source),300);} const erpApiSectionSources={'erp-kingdee-sales':'kingdee_sales_outbound','erp-kingdee-other-outbound':'kingdee_other_outbound','erp-kingdee-other-inbound':'kingdee_other_inbound','erp-kingdee-transfer':'kingdee_transfer','erp-kingdee-accounts-receivable':'kingdee_accounts_receivable','erp-jiaplus-delivery':'jiaplus_delivery','erp-jiaplus-self-ship':'jiaplus_self_ship'}; function erpApiSourceBySection(id){return erpApiSectionSources[id]||'jiaplus_self_ship';} function erpApiPeriodDates(year,month){const f=d=>{const y=d.getFullYear(),m=String(d.getMonth()+1).padStart(2,'0'),day=String(d.getDate()).padStart(2,'0');return `${y}-${m}-${day}`;};return {start:f(new Date(year,month-1,1)),end:f(new Date(year,month,0))};} function erpApiDefaultDates(source){const now=new Date(),value=window.defaultBusinessPeriod||{year:now.getFullYear(),month:now.getMonth()+1},range=erpApiPeriodDates(value.year,value.month),a=document.getElementById(source+'Start'),b=document.getElementById(source+'End');if(a&&!a.value)a.value=range.start;if(b&&!b.value)b.value=range.end;} window.applyErpApiBusinessPeriod=function(year,month){ const range=erpApiPeriodDates(year,month); ['jiaplus_delivery','jiaplus_self_ship'].forEach(source=>{const start=document.getElementById(source+'Start'),end=document.getElementById(source+'End');if(start)start.value=range.start;if(end)end.value=range.end;}); const salesYear=document.getElementById('kingdeeSalesYear'),salesPeriod=document.getElementById('kingdeeSalesPeriod');if(salesYear)salesYear.value=String(year);if(salesPeriod)salesPeriod.value=String(month); kingdeeDocumentSources.forEach(source=>{const yearEl=document.getElementById(source+'Year'),periodEl=document.getElementById(source+'Period');if(yearEl)yearEl.value=String(year);if(periodEl)periodEl.value=String(month);}); kingdeeDefaultPeriodApplied=false;Object.keys(kingdeeDocumentDefaultPeriodApplied).forEach(source=>delete kingdeeDocumentDefaultPeriodApplied[source]); }; function setupJiaplusSelfShipPullUi(){ const source='jiaplus_self_ship',start=document.getElementById(source+'Start'),end=document.getElementById(source+'End'); if(start?.previousElementSibling)start.previousElementSibling.innerText='发货时间(北京)开始'; if(end?.previousElementSibling)end.previousElementSibling.innerText='发货时间(北京)结束'; const batch=document.getElementById(source+'Batch');if(batch){batch.value='';batch.parentElement.style.display='none';} const status=document.getElementById(source+'Status'); const card=status?.closest('.card'); if(card&&!document.getElementById('jiaplusSelfShipDateHint'))card.insertAdjacentHTML('afterbegin','
日期口径:按实际发货时间(北京)查询。重新拉取成功后覆盖上一次自发货预览数据,不保留历史查询批次。
'); } function setupJiaplusDeliveryPullUi(){ const source='jiaplus_delivery',start=document.getElementById(source+'Start'),end=document.getElementById(source+'End'); if(start?.previousElementSibling)start.previousElementSibling.innerText='发货时间(市场时间)开始'; if(end?.previousElementSibling)end.previousElementSibling.innerText='发货时间(市场时间)结束'; const batch=document.getElementById(source+'Batch');if(batch){batch.value='';batch.parentElement.style.display='none';} const status=document.getElementById(source+'Status'),card=status?.closest('.card'); if(card&&!document.getElementById('jiaplusDeliveryDateHint'))card.insertAdjacentHTML('afterbegin','
日期口径:按发货时间(市场时间)校验,单次范围最多 31 天。接口按更新时间分段检索后,只保留目标发货日期;重新拉取成功后覆盖上一次配送报告预览。
'); } async function loadErpJiaplusConfig(){try{const c=await apiGet('/api/erp-api/jiaplus/config');erpJAppId.value=c.app_id||'';erpJAppKey.value=c.app_key||'';erpJBase.value=c.base_url||'';erpJHeaders.value=c.headers_json||'{}';erpJMethod.value=c.method||'POST';erpJTimeout.value=c.timeout||60;erpJDeliveryEndpoint.value=c.delivery_endpoint||'/operation/sale/fbaShipment/page';erpJDeliveryPath.value=c.delivery_data_path||'data.rows';erpJDeliveryBody.value=c.delivery_body_template||'{"page":{page},"pagesize":{page_size},"dateType":0,"updateTimeBegin":"{start_date} 00:00:00","updateTimeEnd":"{end_date} 23:59:59"}';erpJSelfEndpoint.value=c.self_ship_endpoint||'/fulfillment/order/foOrder/page';erpJSelfPath.value=c.self_ship_data_path||'data.rows';erpJSelfBody.value=c.self_ship_body_template||'{"page":{page},"pagesize":{page_size},"deliveryTimeAfter":"{start_date}","deliveryTimeBefore":"{end_date}"}';}catch(e){toast('加载积加配置失败:'+e.message);}} async function saveErpJiaplusConfig(){try{await apiPost('/api/erp-api/jiaplus/config',{app_id:erpJAppId.value.trim(),app_key:erpJAppKey.value.trim(),base_url:erpJBase.value.trim(),headers_json:erpJHeaders.value.trim()||'{}',method:erpJMethod.value,timeout:parseInt(erpJTimeout.value)||60,delivery_endpoint:erpJDeliveryEndpoint.value.trim(),delivery_data_path:erpJDeliveryPath.value.trim(),delivery_body_template:erpJDeliveryBody.value.trim(),self_ship_endpoint:erpJSelfEndpoint.value.trim(),self_ship_data_path:erpJSelfPath.value.trim(),self_ship_body_template:erpJSelfBody.value.trim()});toast('积加API配置已保存');}catch(e){toast(e.message);}} async function testErpJiaplusConfig(){try{const r=await apiPost('/api/erp-api/jiaplus/test',{});toast(r.message);}catch(e){toast(e.message);}} let erpKingdeeDocuments=[]; async function loadErpKingdeeConfig(){try{const [c,documents]=await Promise.all([apiGet('/api/erp-api/kingdee/config'),apiGet('/api/erp-api/kingdee/documents')]);erpKBase.value=c.base_url||'';erpKAcct.value=c.acct_id||'';erpKUser.value=c.username||'';erpKAppId.value=c.app_id||'';erpKSecret.value=c.app_secret||'';erpKLcid.value=c.lcid||2052;erpKTimeout.value=c.timeout||60;erpKingdeeDocuments=documents||[];renderErpKingdeeDocuments();if(!erpKDocId.value)resetErpKingdeeDocumentForm();}catch(e){toast('加载金蝶配置失败:'+e.message);}} function renderErpKingdeeDocuments(){erpKDocTb.innerHTML=erpKingdeeDocuments.map(item=>`${item.sort_order||0}${esc(item.code)}${esc(item.name)}${esc(item.form_id)}${esc(item.organization_field||'-')}${esc(item.date_field||'-')}${esc(item.filter_string||'-')}${item.enabled?'启用':'停用'} ${item.code==='sales_outbound'?'':' '}`).join('')||'
暂无单据配置
';} function resetErpKingdeeDocumentForm(){erpKDocId.value='';erpKDocCode.value='';erpKDocCode.disabled=false;erpKDocName.value='';erpKDocForm.value='';erpKDocOrgField.value='';erpKDocDateField.value='FDate';erpKDocFilter.value='';erpKDocEnabled.value='1';erpKDocSort.value=String((erpKingdeeDocuments.reduce((max,item)=>Math.max(max,Number(item.sort_order)||0),0)||0)+10);erpKDocFields.value='';} function editErpKingdeeDocument(id){const item=erpKingdeeDocuments.find(row=>row.id===id);if(!item)return;erpKDocId.value=item.id;erpKDocCode.value=item.code||'';erpKDocCode.disabled=true;erpKDocName.value=item.name||'';erpKDocForm.value=item.form_id||'';erpKDocOrgField.value=item.organization_field||'';erpKDocDateField.value=item.date_field||'FDate';erpKDocFilter.value=item.filter_string||'';erpKDocEnabled.value=item.enabled?'1':'0';erpKDocSort.value=item.sort_order||0;erpKDocFields.value=item.field_keys||'';document.getElementById('erp-kingdee-config').scrollIntoView({behavior:'smooth',block:'start'});} async function saveErpKingdeeDocument(){try{const payload={id:parseInt(erpKDocId.value)||null,code:erpKDocCode.value.trim(),name:erpKDocName.value.trim(),form_id:erpKDocForm.value.trim(),organization_field:erpKDocOrgField.value.trim(),date_field:erpKDocDateField.value.trim()||'FDate',filter_string:erpKDocFilter.value.trim(),field_keys:erpKDocFields.value.trim(),enabled:parseInt(erpKDocEnabled.value),sort_order:parseInt(erpKDocSort.value)||0};await apiPost('/api/erp-api/kingdee/documents',payload);toast('单据接口配置已保存');resetErpKingdeeDocumentForm();await loadErpKingdeeConfig();}catch(e){toast('保存单据配置失败:'+e.message);}} async function testErpKingdeeDocument(id){try{const result=await apiPost('/api/erp-api/kingdee/documents/'+id+'/test',{});toast(result.message||'测试完成');}catch(e){toast('测试单据失败:'+e.message);}} async function deleteErpKingdeeDocument(id){const item=erpKingdeeDocuments.find(row=>row.id===id);if(!item||!confirm('确定删除单据配置“'+item.name+'”吗?'))return;try{await apiDelete('/api/erp-api/kingdee/documents/'+id,{});toast('单据配置已删除');resetErpKingdeeDocumentForm();await loadErpKingdeeConfig();}catch(e){toast('删除失败:'+e.message);}} async function loadKingdeeSalesOrgs(){try{const rows=await apiGet('/api/erp-api/kingdee/sales-orgs');window.kingdeeSalesOrgs=rows||[];document.getElementById('kingdeeOrgTb').innerHTML=window.kingdeeSalesOrgs.map(item=>`${esc(item.code)}${esc(item.name)}${item.enabled?'启用':'停用'}${item.include_in_reconcile?'参与':'不参与'} `).join('')||'
暂无销售组织
';}catch(e){toast('加载销售组织失败:'+e.message);}} function editKingdeeSalesOrg(id){const item=(window.kingdeeSalesOrgs||[]).find(x=>x.id===id);if(!item)return;document.getElementById('kingdeeOrgCode').value=item.code;document.getElementById('kingdeeOrgName').value=item.name;document.getElementById('kingdeeOrgEnabled').value=item.enabled?'1':'0';document.getElementById('kingdeeOrgReconcile').value=item.include_in_reconcile?'1':'0';document.getElementById('kingdeeOrgCode').dataset.id=id;} async function saveKingdeeSalesOrg(){try{const code=document.getElementById('kingdeeOrgCode').value.trim(),name=document.getElementById('kingdeeOrgName').value.trim(),enabled=parseInt(document.getElementById('kingdeeOrgEnabled').value),include_in_reconcile=parseInt(document.getElementById('kingdeeOrgReconcile').value),id=parseInt(document.getElementById('kingdeeOrgCode').dataset.id||0)||null;if(!code||!name){toast('请填写销售组织编码和名称');return;}await apiPost('/api/erp-api/kingdee/sales-orgs',{id,code,name,enabled,include_in_reconcile});document.getElementById('kingdeeOrgCode').dataset.id='';toast('销售组织已保存');await loadKingdeeSalesOrgs();}catch(e){toast('保存销售组织失败:'+e.message);}} async function deleteKingdeeSalesOrg(id){if(!confirm('确定删除这个销售组织吗?'))return;try{await apiDelete('/api/erp-api/kingdee/sales-orgs/'+id);await loadKingdeeSalesOrgs();}catch(e){toast('删除销售组织失败:'+e.message);}} async function clearKingdeeHistory(){if(!confirm('确定清理全部金蝶销售出库历史拉取数据吗?正式对账数据不会受影响。'))return;try{const r=await apiDelete('/api/erp-api/kingdee/history');toast('已清理 '+(r.deleted_rows||0)+' 条历史明细');await loadKingdeeSalesOrgs();}catch(e){toast('清理历史失败:'+e.message);}} function kingdeeReconcilePeriod(){return {year:parseInt(document.getElementById('kingdeeSalesYear')?.value||0),period:parseInt(document.getElementById('kingdeeSalesPeriod')?.value||0)};} async function checkKingdeeReconcile(notify=true){ try{ const selected=kingdeeReconcilePeriod();if(!selected.year||!selected.period)return; const result=await apiGet('/api/erp-api/kingdee/reconcile-readiness?year='+selected.year+'&period='+selected.period); window.kingdeeReconcileReadiness=result; const source=document.getElementById('kingdeeFormalSource');if(source){source.disabled=!result.current_source_available;source.value=result.current_source_available?(result.current_source||'kingdee'):'';source.title=result.current_source_available?'切换收入核对使用的金蝶正式数据':'API数据发布后可切换正式来源';} const organizations=(result.organizations||[]).map(item=>`${esc(item.code)}:${item.ready?'已拉取 '+item.count+' 条':'缺少成功批次'}`).join(';')||'未配置参与收入核对的组织'; const comparison=result.comparison||{},api=result.api||{},manual=result.manual||{}; const compareText=manual.record_count?`Excel导入正式数据 ${manual.record_count} 条;业务键差异:API缺少 ${comparison.missing_in_api||0},Excel导入缺少 ${comparison.missing_in_manual||0},数量差异 ${comparison.qty_difference_count||0},金额差异 ${comparison.amount_difference_count||0}`:'当前期间没有Excel导入正式数据'; const customerSkipText=result.customer_skip_count?`;客户不核对 ${result.customer_skip_count} 条(保留展示,不参与收入核对)`:''; const invalidText=Object.entries(result.invalid_fields||{}).map(([label,count])=>{const samples=(result.invalid_field_samples||{})[label]||[];return `${esc(label)}为空 ${count} 条${samples.length?'(单据:'+samples.map(esc).join('、')+')':''}`;}).join(';'); const status=document.getElementById('kingdeeReconcileStatus'); if(status){status.style.background=result.ready?'#f0fdf4':'#fef2f2';status.style.borderColor=result.ready?'#bbf7d0':'#fecaca';status.style.color=result.ready?'#15803d':'#b91c1c';status.innerHTML=`收入核对准备:${organizations}
API合并:${api.record_count||0} 条,数量 ${erpKingdeeTotalValue('FRealQty',api.qty||0)},金额 ${erpKingdeeTotalValue('FAmount_LC',api.amount||0)}${customerSkipText};${compareText}${invalidText?'
未通过原因:'+invalidText+' ':''}
当前正式来源:${esc(result.current_source_label||'尚未发布')}`;} if(notify)toast(result.ready?'API数据校验完成,可发布到收入核对':'API数据尚未满足发布条件'); return result; }catch(error){const status=document.getElementById('kingdeeReconcileStatus');if(status)status.innerHTML='收入核对检查失败:'+esc(error.message);if(notify)toast(error.message);} } async function publishKingdeeReconcile(){ const selected=kingdeeReconcilePeriod(),readiness=await checkKingdeeReconcile(false);if(!readiness)return; if(!readiness.ready){const invalid=Object.entries(readiness.invalid_fields||{}).map(([label,count])=>{const samples=(readiness.invalid_field_samples||{})[label]||[];return `${label}为空 ${count} 条${samples.length?'(单据:'+samples.join('、')+')':''}`;}).join(';');toast(invalid?'API字段不完整:'+invalid:'参与收入核对的组织尚未全部拉取成功,不能发布');return;} const organizations=(readiness.organizations||[]).map(item=>item.code).join('、'); if(!confirm(`确定将 ${selected.year} 年 ${selected.period} 月组织 ${organizations} 的API数据合并发布到收入核对吗?\nExcel导入正式数据会保留为备用,不会重复参与核对。`))return; try{showLoading('正在合并并发布金蝶API数据...');const result=await apiPost('/api/erp-api/kingdee/publish',selected);hideLoading();toast(result.message);await checkKingdeeReconcile(false);if(typeof renderSalesOutboundStandard==='function')renderSalesOutboundStandard();}catch(error){hideLoading();toast('发布失败:'+error.message);} } async function changeKingdeeFormalSource(){ const selected=kingdeeReconcilePeriod(),source=document.getElementById('kingdeeFormalSource');if(!source||!source.value)return; try{const result=await apiPost('/api/erp-api/kingdee/reconcile-source',{...selected,source_type:source.value});toast(result.message);await checkKingdeeReconcile(false);if(typeof renderSalesOutboundStandard==='function')renderSalesOutboundStandard();}catch(error){toast('切换失败:'+error.message);await checkKingdeeReconcile(false);} } async function saveErpKingdeeConfig(){try{await apiPost('/api/erp-api/kingdee/config',{base_url:erpKBase.value.trim(),acct_id:erpKAcct.value.trim(),username:erpKUser.value.trim(),app_id:erpKAppId.value.trim(),app_secret:erpKSecret.value.trim(),lcid:parseInt(erpKLcid.value)||2052,timeout:parseInt(erpKTimeout.value)||60});toast('金蝶统一登录配置已保存');await loadErpKingdeeConfig();}catch(e){toast(e.message);}} async function testErpKingdeeConfig(){try{const r=await apiPost('/api/erp-api/kingdee/test',{});toast(r.message);}catch(e){toast(e.message);}} function setupKingdeePullUi(){const source='kingdee_sales_outbound';if(!document.getElementById('kingdeeSalesYear')){const wrap=document.getElementById(source+'Batch')?.parentElement;if(wrap){wrap.insertAdjacentHTML('beforebegin','
');}const currentYear=new Date().getFullYear(),yearEl=document.getElementById('kingdeeSalesYear');yearEl.innerHTML=Array.from({length:7},(_,i)=>currentYear+1-i).map(y=>'').join('');yearEl.value=String(currentYear);document.getElementById('kingdeeSalesPeriod').value=String(new Date().getMonth()+1);const start=document.getElementById(source+'Start')?.parentElement,end=document.getElementById(source+'End')?.parentElement,org=document.getElementById(source+'Org')?.parentElement,batch=document.getElementById(source+'Batch')?.parentElement;if(start)start.style.display='none';if(end)end.style.display='none';if(org)org.style.display='none';if(batch)batch.style.display='none';const status=document.getElementById(source+'Status');if(status&&!document.getElementById('kingdeeReconcileStatus'))status.insertAdjacentHTML('afterend','
正在检查收入核对数据...
');}} let kingdeeDefaultPeriodApplied=false; async function applyKingdeeDefaultPeriod(){if(kingdeeDefaultPeriodApplied)return;try{const value=window.defaultBusinessPeriod||await apiGet('/api/settlement/default-period'),yearEl=document.getElementById('kingdeeSalesYear'),periodEl=document.getElementById('kingdeeSalesPeriod');if(!yearEl||!periodEl)return;const year=String(value.year||''),month=String(value.month||'');if(year&&![...yearEl.options].some(option=>option.value===year))yearEl.add(new Option(year+' 年',year));if(year)yearEl.value=year;if(month)periodEl.value=month;kingdeeDefaultPeriodApplied=true;}catch(error){console.error(error);}} const kingdeeDocumentSources=new Set(['kingdee_other_outbound','kingdee_other_inbound','kingdee_transfer','kingdee_accounts_receivable']); const kingdeeInventorySources=new Set(['kingdee_other_outbound','kingdee_other_inbound','kingdee_transfer']); const kingdeeDocumentDefaultPeriodApplied={}; const kingdeeDocumentFilterTimers={}; function setupKingdeeDocumentPullUi(source){if(!document.getElementById(source+'Year')){const batch=document.getElementById(source+'Batch')?.parentElement;if(batch)batch.insertAdjacentHTML('beforebegin',`
`);}const currentYear=new Date().getFullYear(),yearEl=document.getElementById(source+'Year'),selectedYear=yearEl?.value;yearEl.innerHTML=Array.from({length:7},(_,i)=>currentYear+1-i).map(year=>``).join('');yearEl.value=selectedYear&&[...yearEl.options].some(option=>option.value===selectedYear)?selectedYear:String(currentYear);const periodEl=document.getElementById(source+'Period');if(!periodEl.value)periodEl.value=String(new Date().getMonth()+1);[source+'Start',source+'End',source+'Org',source+'Batch'].forEach(id=>{const parent=document.getElementById(id)?.parentElement;if(parent)parent.style.display='none';});const status=document.getElementById(source+'Status');if(status&&!document.getElementById(source+'ReconcileStatus'))status.insertAdjacentHTML('afterend',`
${kingdeeInventorySources.has(source)?'正在检查库存核对发布数据...':'费用核对逻辑暂未启用;当前数据仅供预览和导出。'}
`);} async function applyKingdeeDocumentDefaultPeriod(source){if(kingdeeDocumentDefaultPeriodApplied[source])return;try{const value=window.defaultBusinessPeriod||await apiGet('/api/settlement/default-period'),yearEl=document.getElementById(source+'Year'),periodEl=document.getElementById(source+'Period');if(!yearEl||!periodEl)return;const year=String(value.year||''),month=String(value.month||'');if(year&&![...yearEl.options].some(option=>option.value===year))yearEl.add(new Option(year+' 年',year));if(year)yearEl.value=year;if(month)periodEl.value=month;kingdeeDocumentDefaultPeriodApplied[source]=true;}catch(error){console.error(error);}} async function loadKingdeeDocumentOrgs(source){try{const rows=await apiGet('/api/erp-api/kingdee/sales-orgs'),el=document.getElementById(source+'OrgSelect');if(!el)return;const enabled=(rows||[]).filter(item=>item.enabled),selected=el.value||'all',firstLoad=el.dataset.loaded!=='1',defaultOrg=enabled.find(item=>String(item.code)==='105'),target=firstLoad&&defaultOrg?String(defaultOrg.id):selected;el.innerHTML=''+enabled.map(item=>``).join('');el.value=[...el.options].some(option=>option.value===target)?target:(defaultOrg?String(defaultOrg.id):'all');el.dataset.loaded='1';}catch(error){toast('加载组织失败:'+erpApiFriendlyError(error));}} function kingdeeDocumentPeriod(source){return {year:parseInt(document.getElementById(source+'Year')?.value||0),period:parseInt(document.getElementById(source+'Period')?.value||0)};} function onKingdeeDocumentFilterChange(source,check=true){erpApiPages[source]=1;renderErpApiRecords(source);if(check&&kingdeeInventorySources.has(source))checkKingdeeInventory(source,false);} function scheduleKingdeeDocumentFilter(source){clearTimeout(kingdeeDocumentFilterTimers[source]);kingdeeDocumentFilterTimers[source]=setTimeout(()=>onKingdeeDocumentFilterChange(source,false),300);} function showInvalidKingdeeDocumentRows(source){const org=document.getElementById(source+'OrgSelect'),bill=document.getElementById(source+'BillSearch'),status=document.getElementById(source+'StatusFilter');if(org)org.value='all';if(bill)bill.value='';if(status)status.value='invalid';erpApiPages[source]=1;renderErpApiRecords(source);} async function checkKingdeeInventory(source,notify=true){if(!kingdeeInventorySources.has(source))return null;const selected=kingdeeDocumentPeriod(source),status=document.getElementById(source+'ReconcileStatus');try{const result=await apiGet(`/api/erp-api/kingdee/inventory-readiness/${source}?year=${selected.year}&period=${selected.period}`),organizations=(result.organizations||[]).map(item=>`${esc(item.code)}:${item.ready?item.count+' 条':'未拉取'}`).join(';')||'未维护启用的组织105',invalid=Object.entries(result.invalid_fields||{}).map(([label,count])=>`${esc(label)}为空 ${count} 条`).join(';');if(status){status.style.background=result.ready?'#f0fdf4':'#fef2f2';status.style.borderColor=result.ready?'#bbf7d0':'#fecaca';status.style.color=result.ready?'#15803d':'#b91c1c';status.innerHTML=`库存核对准备(组织105):${organizations}
可发布:${result.record_count||0} 条${invalid?'
未通过原因:'+invalid+` `:''}`;}if(notify)toast(result.ready?'组织105的API数据校验完成,可发布到库存核对':'组织105的API数据尚未满足发布条件');return result;}catch(error){const message=erpApiFriendlyError(error);if(status)status.innerHTML='库存核对检查失败:'+esc(message);if(notify)toast(message);return null;}} async function publishKingdeeInventory(source){const selected=kingdeeDocumentPeriod(source),readiness=await checkKingdeeInventory(source,false);if(!readiness)return;if(!readiness.ready){toast('启用组织尚未全部拉取成功或数据字段不完整,不能发布');return;}if(!confirm(`确定发布 ${selected.year} 年 ${selected.period} 月的金蝶API数据到库存核对吗?\n该期间对应单据类型的金蝶Excel/API正式数据将被替换。`))return;try{showLoading('正在替换并发布金蝶API数据...');const result=await apiPost('/api/erp-api/kingdee/inventory-publish/'+source,selected);hideLoading();toast(result.message);await checkKingdeeInventory(source,false);}catch(error){hideLoading();toast('发布失败:'+error.message);}} async function exportKingdeeDocumentPreview(source){const selected=kingdeeDocumentPeriod(source),orgValue=document.getElementById(source+'OrgSelect')?.value||'all',salesOrgId=orgValue==='all'?0:parseInt(orgValue||0);try{const response=await fetch(`/api/erp-api/kingdee/export/${source}?year=${selected.year}&period=${selected.period}&sales_org_id=${salesOrgId}`,{headers:authHeaders()});if(!response.ok){const payload=await response.json().catch(()=>({detail:'导出失败'}));throw new Error(payload.detail||'导出失败');}const blob=await response.blob(),url=URL.createObjectURL(blob),anchor=document.createElement('a');anchor.href=url;anchor.download=`${source}_${selected.year}_${String(selected.period).padStart(2,'0')}.xlsx`;document.body.appendChild(anchor);anchor.click();anchor.remove();URL.revokeObjectURL(url);}catch(error){toast(error.message);}} async function loadErpApiPreview(source){if(source==='kingdee_sales_outbound'){setupKingdeePullUi();await applyKingdeeDefaultPeriod();await loadKingdeeSalesOrgsForPull();await checkKingdeeReconcile(false);}else if(kingdeeDocumentSources.has(source)){setupKingdeeDocumentPullUi(source);await applyKingdeeDocumentDefaultPeriod(source);await loadKingdeeDocumentOrgs(source);if(kingdeeInventorySources.has(source))await checkKingdeeInventory(source,false);}else{if(source==='jiaplus_self_ship')setupJiaplusSelfShipPullUi();if(source==='jiaplus_delivery')setupJiaplusDeliveryPullUi();erpApiDefaultDates(source);}renderErpApiRecords(source);pollErpApiJob(source);} async function loadKingdeeSalesOrgsForPull(){try{const rows=await apiGet('/api/erp-api/kingdee/sales-orgs');const el=document.getElementById('kingdeeSalesOrg');if(!el)return;const selected=el.value||'all';el.innerHTML=''+(rows||[]).filter(item=>item.enabled).map(item=>``).join('');el.value=[...el.options].some(option=>option.value===selected)?selected:'all';}catch(e){toast('加载销售组织失败:'+e.message);}} function onKingdeePullFilterChange(){erpApiPages.kingdee_sales_outbound=1;renderErpApiRecords('kingdee_sales_outbound');checkKingdeeReconcile(false);} let kingdeeBillSearchTimer=null; function onKingdeeBillSearch(){clearTimeout(kingdeeBillSearchTimer);kingdeeBillSearchTimer=setTimeout(()=>{erpApiPages.kingdee_sales_outbound=1;renderErpApiRecords('kingdee_sales_outbound');},300);} function onKingdeeStatusFilterChange(){erpApiPages.kingdee_sales_outbound=1;renderErpApiRecords('kingdee_sales_outbound');} function showInvalidKingdeeRows(){const org=document.getElementById('kingdeeSalesOrg'),bill=document.getElementById('kingdeeBillNoSearch'),status=document.getElementById('kingdeeRecordStatus');if(org)org.value='all';if(bill)bill.value='';if(status)status.value='invalid';erpApiPages.kingdee_sales_outbound=1;renderErpApiRecords('kingdee_sales_outbound');} async function loadErpApiBatches(source){try{const rows=await apiGet('/api/erp-api/batches/'+source);erpApiBatchCache[source]=rows;const el=document.getElementById(source+'Batch');const selected=el.value;el.innerHTML=''+rows.map(b=>``).join('');if([...el.options].some(o=>o.value===selected))el.value=selected;}catch(e){toast(e.message);}} function erpApiFriendlyError(error){const message=String(error&&error.message||error||'未知错误');return message.includes('404')?'后台服务尚未加载本次新增接口,请重启本地服务后刷新页面':message;} function showErpApiStatus(source,status,message){const el=document.getElementById(source+'Status');if(!el)return;const failed=status==='failed',success=status==='success';el.style.background=failed?'#fef2f2':success?'#f0fdf4':'#eff6ff';el.style.borderColor=failed?'#fecaca':success?'#bbf7d0':'#bfdbfe';el.style.color=failed?'#b91c1c':success?'#15803d':'#1d4ed8';el.innerHTML=`${failed?'拉取失败':success?'拉取成功':'后台任务'}:${esc(message||status||'尚未拉取')}`;} function onErpApiBatchChange(source){erpApiPages[source]=1;const id=parseInt(document.getElementById(source+'Batch').value||0),batch=erpApiBatchCache[source].find(x=>x.id===id);if(batch)showErpApiStatus(source,batch.status,batch.message);renderErpApiRecords(source);} async function startErpApiPull(source){try{const btn=document.getElementById(source+'PullBtn');let body;if(source==='kingdee_sales_outbound'){const year=parseInt(document.getElementById('kingdeeSalesYear').value),period=parseInt(document.getElementById('kingdeeSalesPeriod').value),orgValue=document.getElementById('kingdeeSalesOrg').value,salesOrgId=parseInt(orgValue||0);if(orgValue==='all'){toast('“全部销售组织”仅用于查看,拉取时请选择具体组织');return;}if(!year||!period||!salesOrgId){toast('请选择年度、期间和销售组织');return;}body={year,period,sales_org_id:salesOrgId};}else if(kingdeeDocumentSources.has(source)){const {year,period}=kingdeeDocumentPeriod(source),orgValue=document.getElementById(source+'OrgSelect')?.value||'all',salesOrgId=parseInt(orgValue||0);if(orgValue==='all'){toast('“全部组织”仅用于查看,拉取时请选择具体组织');return;}if(!year||!period||!salesOrgId){toast('请选择年度、期间和组织');return;}body={year,period,sales_org_id:salesOrgId};}else{const start=document.getElementById(source+'Start').value,end=document.getElementById(source+'End').value;body={start_date:start,end_date:end,sales_org:''};}btn.disabled=true;btn.innerText='拉取中...';showErpApiStatus(source,'running','正在连接接口并拉取数据');const r=await apiPost('/api/erp-api/pull/'+source,body);erpApiLastJobStatus[source]='running';toast(r.accepted?'后台拉取任务已提交':'该数据源正在拉取');pollErpApiJob(source);}catch(e){const btn=document.getElementById(source+'PullBtn');btn.disabled=false;btn.innerText='后台拉取';showErpApiStatus(source,'failed',e.message);toast('提交失败:'+e.message);}} async function pollErpApiJob(source){try{const s=await apiGet('/api/erp-api/jobs/'+source),previous=erpApiLastJobStatus[source],btn=document.getElementById(source+'PullBtn');showErpApiStatus(source,s.status,s.message);btn.disabled=!!s.running;btn.innerText=s.running?'拉取中...':'后台拉取';erpApiLastJobStatus[source]=s.status;if(s.running){setTimeout(()=>pollErpApiJob(source),3000);}else if(s.batch_id){if(source==='kingdee_sales_outbound')await checkKingdeeReconcile(false);else if(kingdeeInventorySources.has(source))await checkKingdeeInventory(source,false);renderErpApiRecords(source);if(previous==='running')toast((s.status==='failed'?'拉取失败:':'拉取完成:')+(s.message||''));}}catch(e){showErpApiStatus(source,'failed',erpApiFriendlyError(e));}} const erpKingdeeColumns=[ ['FID','表头内码'],['FEntity_FEntryID','明细内码'], ['FBillNo','单据编号'],['FDate','出库日期','date'],['FSaleOrgId.FNumber','销售组织编码'], ['FSaleOrgId.FName','销售组织名称'],['FCustomerID.FName','客户名称'], ['FMaterialID.FNumber','物料编码'],['FMaterialID.FName','物料名称'],['FRealQty','数量'],['FPrice','单价'],['FAmount','金额'], ['FAmount_LC','金额本位币'],['FTaxAmount','税额'],['FAllAmount','价税合计'], ['FAllAmount_LC','价税合计本位币'],['FSettleCurrID.FName','结算币别'],['FLocalCurrID.FName','本位币'], ['FCreatorId.FName','创建人'],['FPlatform','平台'],['FSourceBillNo','来源单据'], ['FSourceBillType','来源单据类型'],['FPlatformOrderNo','平台订单号'],['FStockID.FName','仓库'] ]; const erpKingdeeRequiredPreviewFields=[['FPlatformOrderNo','平台订单号'],['FBillNo','单据编号'],['FMaterialID.FNumber','物料编码'],['FStockID.FName','仓库'],['FCustomerID.FName','客户名称']]; function erpKingdeeMissingFields(data){return erpKingdeeRequiredPreviewFields.filter(([key])=>!String(erpApiCellValue(data,key)||'').trim()).map(([,label])=>label);} const erpJiaplusSelfShipFieldLabels={ isCustomizationOrder:'是否定制化订单',sourceChannel:'来源渠道',shopName:'店铺/站点', sellerShipmentEndTime:'卖家发货截止时间',orderTime:'订单订购时间',buyerPayTax:'买家支付税费', buyerPayShipFee:'买家支付运费',soOrderNo:'销售订单号(内部)',countryCode:'国家/地区', currency:'币种',id:'订单内部ID',shopId:'店铺ID',shopCountryCode:'店铺国家/地区', shipmentLevel:'配送等级',actualAmount:'实际支付金额',sellerRemark:'卖家备注', totalAmount:'订单总金额',buyerInfo:'买家信息',discountCode:'折扣码', createTime:'系统创建时间',morderType:'配送方式',consigneeInfo:'收货人信息', sourceOrderNo:'平台单号',orderPerformingParty:'订单履约方', orderType:'订单类型',discountFee:'折扣金额', paymentMarketTime:'付款时间(当地)',otherTotalAmount:'其他总金额',erpShopName:'店铺名称', orderDeliveryStatus:'发货状态',omsPushErrorMessage:'OMS推送信息',receiverMobilePhone:'收件人手机', orderingTime:'下单时间(北京时间)',buyerAccountId:'买家账号ID',pointsPayment:'积分支付金额', receiverPhone:'收件人电话',platformWarehouseName:'发货仓',parentOrderNumber:'父订单号', id:'订单内部ID',refundedTotalAmount:'退款总金额',customerMemo:'客户备注',regionCnName:'区域名称', platformOrderPaymentStatus:'平台付款状态',estimateLatestDeliveryMarketTime:'预计最晚送达时间(当地)', orderingMarketTime:'下单时间(当地)',omsPushStatus:'OMS推送状态码',tax:'税费', receiverAddressPostCode:'收件邮编',platformOriginOrderStatus:'平台原始订单状态',totalAmount:'订单总金额', orderCancelStatusName:'取消状态',platformOrderDeliveryTime:'实际发货时间', deliveryTime:'实际发货时间',deliveryTimeMarket:'实际发货时间(当地)', foOrderNo:'系统订单号',foOrderStatus:'订单状态',bizStatus:'业务状态', warehouseName:'发货仓库',warehouseId:'仓库ID', accessMode:'接入方式',orderTotalAmountExcludingTax:'订单售价(不含税)', discount:'订单折扣',points:'订单积分',shippingAmount:'订单其他费用', paymentTime:'订单付款时间',paymentTimeMarket:'订单付款时间(当地)', orderTimeMarket:'订单订购时间(当地)',orderTypeName:'订单类型', deliveryMethod:'配送方式',taxAmount:'订单税金',warehouseRefCode:'仓库单号', platformOrderDeliveryMarketTime:'平台发货时间(当地)',platformOrderNumber:'平台订单号', receiverAddressCountry:'国家/地区',__logisticsCarrier:'物流承运商', __trackingNumber:'跟踪号(运单号)',__logisticsChannel:'物流渠道', __detailThirdSku:'三方仓SKU',__detailMsku:'MSKU',__detailSku:'SKU',__detailAsin:'ASIN/ItemID', __detailCommodityName:'商品名称',__detailUnitPrice:'商品单价', __detailQuantity:'订购数量',__detailExpectedQuantity:'应发量',__detailValidQuantity:'已发量', __detailDiscount:'商品折扣',__detailBuyerPayShipFee:'商品买家支付运费', __detailTaxAmount:'商品税金',__detailShippingAmount:'商品其他费用',__detailTotal:'总额', __detailSourceLineNo:'来源行号',__detailOutOfStockFlag:'是否缺货', __detailLineNo:'订单明细行号',__assignedWarehouseName:'平台仓库名称', __assignedCarrierCode:'平台承运商编码',__assignedWarehouseId:'平台仓库ID' }; const erpJiaplusSelfShipExtraFields=[ '__logisticsCarrier','__trackingNumber','__logisticsChannel','__detailThirdSku','__detailMsku','__detailSku', '__detailAsin','__detailCommodityName','__detailUnitPrice', '__detailQuantity','__detailExpectedQuantity','__detailValidQuantity','__detailDiscount', '__detailBuyerPayShipFee','__detailTaxAmount','__detailShippingAmount','__detailTotal', '__detailSourceLineNo','__detailOutOfStockFlag','__detailLineNo','__assignedWarehouseName', '__assignedCarrierCode','__assignedWarehouseId' ]; const erpJiaplusSelfShipNestedFields=['soOrderDetails','orderItems','platformAssign','buyerInfo','consigneeInfo','foOrderLogisticsInfoVO']; const erpJiaplusDeliveryFields=[ ['__deliveryShop','店铺/站点',['marketName','shopSiteName','shopName','storeName']], ['__deliveryOrderNo','订单编号',['orderId','amazonOrderId','orderNo','sourceOrderNo']], ['__deliveryTitle','商品标题',['listingTitle','productTitle','itemTitle','title','commodityName']], ['__deliveryAsin','ASIN',['asin','asinCode']], ['__deliveryMsku','MSKU',['msku','sellerSku']], ['__deliveryProductName','产品名称',['produceName','productName','skuName']], ['__deliverySku','SKU',['sku','productSku']], ['__deliveryParentProduct','父产品名称',['parentProductName','parentSkuName']], ['__deliverySpu','SPU',['spu','spuCode']], ['__deliveryOperationLevel','商品运营级别',['operationLevel','productOperationLevel']], ['__deliverySellerOrderNo','卖家订单编号',['sellerOrderId','sellerOrderNo']], ['__deliveryOrderType','订单类型',['orderTypeName','orderType']], ['__deliveryReconcileStatus','核对状态',[]], ['__deliveryShipmentId','货件ID',['shipmentId','shipmentNo']], ['__deliveryOrderItemId','订单商品编码',['orderItemId','orderItemCode']], ['__deliveryOrderTimeMarket','订购时间(市场时间)',['purchaseDate','orderTimeMarket','purchaseDateMarket','purchaseDateLocal']], ['__deliveryOrderTimeUtc','订购时间(零时区)',['purchaseDateZero','orderTimeZero','purchaseDateUtc']], ['__deliveryPaymentTimeMarket','付款时间(市场时间)',['paymentsDate','paymentTimeMarket','paymentsDateMarket','paymentsDateLocal']], ['__deliveryPaymentTimeUtc','付款时间(零时区)',['paymentsDateZero','paymentTimeZero','paymentsDateUtc']], ['__deliverySalesChannel','销售渠道',['salesChannel','channelName']], ['__deliveryCurrency','币种',['currency','currencyCode']], ['__deliveryItemPrice','商品售价',['itemPrice','productAmount','salePrice']], ['__deliveryItemTax','税费',['itemTax','itemTaxAmount','taxAmount','tax']], ['__deliveryShippingPrice','运费',['shippingPrice','shippingAmount','freight']], ['__deliveryShippingTax','运费税',['shippingTax','freightTax']], ['__deliveryGiftWrapPrice','礼品包装费',['giftWrapPrice','giftWrapAmount']], ['__deliveryGiftWrapTax','礼品包装税',['giftWrapTax']], ['__deliveryItemPromotion','商品促销折扣',['itemPromotionDiscount','promotionDiscount']], ['__deliveryShippingDiscount','运费折扣',['shipPromotionDiscount','shippingDiscount']], ['__deliveryTotal','总额',[]], ['__deliveryQuantity','发货数量',['quantityShipped','shipmentQuantity','quantity']], ['__deliveryShipmentMarket','发货时间(市场时间)',['shipmentDate','shipmentDateMarket','shipmentTimeMarket','shipmentDateLocal','shipmentTimeLocal','deliveryTimeMarket','shippingTimeMarket','shipDateMarket','shipmentMarketTime']], ['__deliveryShipmentUtc','发货时间(零时区)',['shipmentDateZero','shipmentTimeZero','shipmentDateUtc']], ['__deliveryCarrier','承运人',['carrier','carrierName']], ['__deliveryTracking','追踪号码',['trackingNumber','trackingNo']], ['__deliveryArrivalMarket','预计到达时间(市场时间)',['estimatedArrivalDate','estimatedArrivalDateMarket','estimatedArrivalTimeMarket','estimatedArrivalDateLocal']], ['__deliveryArrivalUtc','预计到达时间(零时区)',['estimatedArrivalDateZero','estimatedArrivalTimeZero','estimatedArrivalDateUtc']], ['__deliveryCenterId','运营中心ID',['fulfillmentCenterId','operationCenterId']], ['__deliveryWarehouse','配送仓库名称',['shipmentWarehouseName','warehouseName','deliveryWarehouseName']], ['__deliveryService','配送服务',['shipServiceLevel','deliveryService']], ['__deliveryBuyerEmail','买家邮箱',['buyerEmail']], ['__deliveryCity','收货城市',['shipCity','shippingCity']], ['__deliveryState','收货地区',['shipState','shippingState']], ['__deliveryCountry','收货国家/地区',['shipCountry','shippingCountry','countryCode']], ['__deliveryUpdateTime','更新时间',['updateDate','updateTime','updatedAt']] ]; const erpJiaplusDeliveryFieldMap=Object.fromEntries(erpJiaplusDeliveryFields.map(([key,label,aliases])=>[key,{label,aliases}])); const erpJiaplusDeliveryMoneyFields=new Set(['__deliveryItemPrice','__deliveryItemTax','__deliveryShippingPrice','__deliveryShippingTax','__deliveryGiftWrapPrice','__deliveryGiftWrapTax','__deliveryItemPromotion','__deliveryShippingDiscount']); function erpDeliveryFirstValue(data,aliases){for(const key of aliases){const value=data&&data[key];if(value!==null&&value!==undefined&&value!=='')return value;}return '';} function erpJiaplusNumber(value){if(value&&typeof value==='object')value=value.currencyAmount??value.amount??value.value??0;const number=Number(String(value??0).replace(/,/g,''));return Number.isFinite(number)?number:0;} function erpJiaplusAmount(value){return erpJiaplusNumber(value).toLocaleString('zh-CN',{minimumFractionDigits:2,maximumFractionDigits:2});} function erpDeliveryCellValue(data,key){ const field=erpJiaplusDeliveryFieldMap[key];if(!field)return ''; if(key==='__deliveryReconcileStatus'){ const orderType=String(erpDeliveryFirstValue(data,['orderTypeName','orderType'])||'').toLowerCase(); return ['multichannel','multichannelorder','multi_channel_order','多渠道订单'].includes(orderType)?'不核对(多渠道订单)':'参与核对'; } if(key==='__deliveryTotal')return erpJiaplusAmount( erpJiaplusNumber(erpDeliveryFirstValue(data,['itemPrice','productAmount','salePrice']))+ erpJiaplusNumber(erpDeliveryFirstValue(data,['shippingPrice','shippingAmount','freight']))+ erpJiaplusNumber(erpDeliveryFirstValue(data,['giftWrapPrice','giftWrapAmount']))+ erpJiaplusNumber(erpDeliveryFirstValue(data,['itemPromotionDiscount','promotionDiscount']))+ erpJiaplusNumber(erpDeliveryFirstValue(data,['shipPromotionDiscount','shippingDiscount'])) ); if(key==='__deliveryShop'){ const direct=String(erpDeliveryFirstValue(data,['marketName','shopSiteName'])||''),name=String(erpDeliveryFirstValue(data,['shopName','storeName'])||''),country=String(erpDeliveryFirstValue(data,['shopCountry','shopCountryCode','marketCountryCode'])||''); if(direct)return direct; return name&&country&&!name.toUpperCase().endsWith(':'+country.toUpperCase())?name+':'+country:name||country; } const value=erpDeliveryFirstValue(data,field.aliases); if(key==='__deliveryOrderType')return ({StandardOrder:'亚马逊销售订单',Multichannel:'多渠道订单',MultiChannelOrder:'多渠道订单',Replacement:'换货订单',ReplacementOrder:'换货订单'})[value]||value; if(erpJiaplusDeliveryMoneyFields.has(key))return erpJiaplusAmount(value); if(value&&typeof value==='object')return value.currencyAmount??value.amount??value.value??JSON.stringify(value); return value; } function erpSelfShipTrackingParts(data){ const logistics=data&&data.foOrderLogisticsInfoVO; if(logistics){return [ logistics.actualCarrier||logistics.logisticsName||'', logistics.trackingNo||logistics.waybillNo||'', logistics.logisticsChannelName||logistics.logisticsChannel||'' ];} const result=[[],[],[]]; (data&&Array.isArray(data.items)?data.items:[]).forEach(item=>{ const values=Array.isArray(item.trackingNumberList)&&item.trackingNumberList.length?item.trackingNumberList:[item.trackingNumber]; values.filter(Boolean).forEach(raw=>{ const parts=String(raw).split('&'); const normalized=parts.length>=3?[parts[0],parts[1],parts[2]]:['',parts[0],'']; normalized.forEach((value,index)=>{ if(value&&!result[index].includes(value))result[index].push(value); }); }); }); return result.map(values=>values.join(';')); } function erpSelfShipDetailValues(data,field){ const details=data&&(Array.isArray(data.orderItems)?data.orderItems:Array.isArray(data.soOrderDetails)?data.soOrderDetails:[]); const fields=Array.isArray(field)?field:[field]; return details .map(item=>{for(const key of fields){if(item&&item[key]!=null&&item[key]!=='')return item[key];}return '';}) .filter(value=>value!==null&&value!==undefined&&value!=='') .map(String).join(';'); } function erpSelfShipDetailTotals(data){ const details=data&&(Array.isArray(data.orderItems)?data.orderItems:Array.isArray(data.soOrderDetails)?data.soOrderDetails:[]); return details.map(item=>erpJiaplusAmount( erpJiaplusNumber(item&&item.unitPrice)*erpJiaplusNumber(item&&item.quantity)- erpJiaplusNumber(item&&item.discount)+erpJiaplusNumber(item&&item.buyerPayShipFee) )).join(';'); } function erpApiCellValue(data,key,format){ let value=''; if(key.startsWith('__delivery')){ value=erpDeliveryCellValue(data,key); }else if(key==='shopName'){ const name=String(data?.shopName||data?.erpShopName||''),country=String(data?.shopCountry||data?.shopCountryCode||(data?.sourceChannel==='SHOPIFY'?'Global':'')||''); value=name&&country&&!name.toUpperCase().endsWith(':'+country.toUpperCase())?name+':'+country:name||country; }else if(key==='foOrderStatus'){ value=({ALREADY_DELIVERY:'已发货',AWAIT_DELIVERY:'待发货',CANCELLED:'已取消'})[data?.foOrderStatus]||data?.foOrderStatus||''; }else if(key==='deliveryMethod'){ value=({SELLER:'卖家自配送',PLATFORM:'平台配送'})[data?.deliveryMethod]||data?.deliveryMethod||''; }else if(key==='__logisticsCarrier'||key==='__trackingNumber'||key==='__logisticsChannel'){ const index={__logisticsCarrier:0,__trackingNumber:1,__logisticsChannel:2}[key]; value=erpSelfShipTrackingParts(data)[index]; }else if(key==='receiverAddressCountry'){ const country=data&&data.receiverAddressCountry||'',code=data&&data.receiverAddressCountryCode||''; value=country+(country&&code?'('+code+')':code); }else if(key==='__detailTotal'){ value=erpSelfShipDetailTotals(data); }else if(key.startsWith('__detail')){ const field={ __detailThirdSku:'thirdSku',__detailMsku:'msku',__detailSku:'sku',__detailAsin:'asin', __detailCommodityName:'commodityName',__detailUnitPrice:'unitPrice', __detailQuantity:'quantity',__detailExpectedQuantity:'expectedQuantity',__detailValidQuantity:'validQuantity', __detailDiscount:'discount',__detailBuyerPayShipFee:'buyerPayShipFee', __detailTaxAmount:'taxAmount',__detailShippingAmount:'shippingAmount', __detailSourceLineNo:'sourceLineNo',__detailOutOfStockFlag:'outOfStockFlag', __detailLineNo:['foOrderLineNo','packageLineNo','soOrderLineNo'] }[key]; value=erpSelfShipDetailValues(data,field); if(key==='__detailOutOfStockFlag')value=String(value).split(';').map(item=>item==='Y'?'是':item==='N'?'否':item).join(';'); }else if(key.startsWith('__assigned')){ const field={ __assignedWarehouseName:'platformWarehouseName', __assignedCarrierCode:'platformCarrierCode', __assignedWarehouseId:'platformWarehouseId' }[key]; value=data&&data.platformAssign&&data.platformAssign[field]!=null?data.platformAssign[field]:''; }else if(key.includes('.')){ value=data&&Object.prototype.hasOwnProperty.call(data,key)?data[key]:key.split('.').reduce((current,part)=>current&¤t[part]!=null?current[part]:'',data); }else value=data&&data[key]!=null?data[key]:''; if(format==='date'&&value)value=String(value).slice(0,10); return typeof value==='object'?JSON.stringify(value):value; } const erpKingdeeTotalFields=new Set(['FRealQty','FAmount_LC','FTaxAmount','FAllAmount','FAllAmount_LC']); function erpKingdeeTotalValue(key,value){ const number=Number(value||0); return number.toLocaleString('zh-CN',key==='FRealQty'?{maximumFractionDigits:6}:{minimumFractionDigits:2,maximumFractionDigits:2}); } function renderErpKingdeeTotals(foot,totals,total){ if(!foot)return; foot.innerHTML='合计(全部 '+total+' 条)'+erpKingdeeColumns.map(column=>{ const key=column[0],value=erpKingdeeTotalFields.has(key)?erpKingdeeTotalValue(key,totals&&totals[key]):''; return `${esc(value)}`; }).join('')+''; } async function exportKingdeeSalesOutbound(){ try{ const year=parseInt(document.getElementById('kingdeeSalesYear').value),period=parseInt(document.getElementById('kingdeeSalesPeriod').value),orgValue=document.getElementById('kingdeeSalesOrg').value,salesOrgId=parseInt(orgValue||0); if(orgValue==='all'){toast('导出时请选择一个具体销售组织');return;} if(!year||!period||!salesOrgId){toast('请选择年度、期间和销售组织');return;} showLoading('正在导出金蝶销售出库API数据...'); const url='/api/erp-api/kingdee/export?year='+year+'&period='+period+'&sales_org_id='+salesOrgId; const response=await fetch(url,{headers:authHeaders()}); if(!response.ok){let message='导出失败:HTTP '+response.status;try{const error=await response.json();if(error.detail)message=error.detail;}catch(_error){}throw new Error(message);} const blob=await response.blob(),downloadUrl=URL.createObjectURL(blob),anchor=document.createElement('a'); anchor.href=downloadUrl;anchor.download='kingdee_sales_outbound_'+year+'_'+String(period).padStart(2,'0')+'.xlsx';document.body.appendChild(anchor);anchor.click(); setTimeout(()=>{URL.revokeObjectURL(downloadUrl);anchor.remove();},1000);hideLoading();toast('导出成功'); }catch(error){hideLoading();toast(error.message);} } async function exportJiaplusPreview(source){ try{ const labels={jiaplus_delivery:'积加配送报告',jiaplus_self_ship:'积加自发货订单'},label=labels[source]; if(!label){toast('不支持的积加导出类型');return;} showLoading('正在导出'+label+'全量数据...'); const response=await fetch('/api/erp-api/jiaplus/export/'+source,{headers:authHeaders()}); if(!response.ok){let message='导出失败:HTTP '+response.status;try{const error=await response.json();if(error.detail)message=error.detail;}catch(_error){}throw new Error(message);} const start=document.getElementById(source+'Start')?.value||'',end=document.getElementById(source+'End')?.value||'',disposition=response.headers.get('Content-Disposition')||'',filenameMatch=disposition.match(/filename="?([^";]+)"?/i),blob=await response.blob(),downloadUrl=URL.createObjectURL(blob),anchor=document.createElement('a'); anchor.href=downloadUrl;anchor.download=filenameMatch?filenameMatch[1]:label+'_'+start+'_'+end+'.xlsx';document.body.appendChild(anchor);anchor.click(); setTimeout(()=>{URL.revokeObjectURL(downloadUrl);anchor.remove();},1000);hideLoading();toast('导出成功'); }catch(error){hideLoading();toast(error.message);} } async function publishJiaplusPreview(source){ const labels={jiaplus_delivery:'积加配送报告',jiaplus_self_ship:'积加自发货订单'},label=labels[source]; if(!label){toast('不支持的积加发布类型');return;} if(!confirm('确认将当前'+label+'预览发布到销售出库数据的积加页签?重复业务明细将被覆盖。'))return; try{ showLoading('正在发布'+label+'...'); const result=await apiPost('/api/erp-api/jiaplus/publish/'+source,{}); hideLoading();toast(result.message||'发布完成'); }catch(error){hideLoading();toast(error.message);} } function erpJiaplusStatusHtml(row){ if(row.record_status==='skipped')return '不核对'; if(row.record_status==='invalid')return '字段不完整'; return '字段完整'; } async function renderErpApiRecords(source){ try{ const batch=document.getElementById(source+'Batch').value; let url='/api/erp-api/records/'+source+'?page='+erpApiPages[source]+'&page_size='+erpApiPageSizes[source]; if(batch)url+='&batch_id='+batch; if(source==='kingdee_sales_outbound'){const year=parseInt(document.getElementById('kingdeeSalesYear')?.value||0),period=parseInt(document.getElementById('kingdeeSalesPeriod')?.value||0),orgValue=document.getElementById('kingdeeSalesOrg')?.value||'all',salesOrgId=parseInt(orgValue||0),billNo=document.getElementById('kingdeeBillNoSearch')?.value.trim()||'',recordStatus=document.getElementById('kingdeeRecordStatus')?.value||'all';if(year&&period){url+='&year='+year+'&period='+period;if(orgValue==='all')url+='&all_sales_orgs=true';else if(salesOrgId)url+='&sales_org_id='+salesOrgId;if(billNo)url+='&bill_no='+encodeURIComponent(billNo);if(recordStatus!=='all')url+='&record_status='+recordStatus;}} else if(kingdeeDocumentSources.has(source)){const {year,period}=kingdeeDocumentPeriod(source),orgValue=document.getElementById(source+'OrgSelect')?.value||'all',salesOrgId=parseInt(orgValue||0),billNo=document.getElementById(source+'BillSearch')?.value.trim()||'',recordStatus=document.getElementById(source+'StatusFilter')?.value||'all';if(year&&period){url+='&year='+year+'&period='+period;if(orgValue==='all')url+='&all_sales_orgs=true';else if(salesOrgId)url+='&sales_org_id='+salesOrgId;if(billNo)url+='&bill_no='+encodeURIComponent(billNo);if(recordStatus!=='all')url+='&record_status='+recordStatus;}} else if(source==='jiaplus_delivery'||source==='jiaplus_self_ship'){const orderSearch=document.getElementById(source+'OrderSearch')?.value.trim()||'',recordStatus=document.getElementById(source+'RecordStatus')?.value||'all';if(orderSearch)url+='&bill_no='+encodeURIComponent(orderSearch);if(recordStatus!=='all')url+='&record_status='+recordStatus;} const r=await apiGet(url),rows=r.rows||[],head=document.getElementById(source+'Head'),body=document.getElementById(source+'Body'),foot=document.getElementById(source+'Total'); erpApiTotals[source]=r.total||0; if(source==='kingdee_sales_outbound'){ head.innerHTML='序号数据状态'+erpKingdeeColumns.map(c=>{ const hint=c[0]==='FLocalCurrID.FName'?'取自表头财务页签,显示名称':(['FPrice','FAmount','FAmount_LC','FTaxAmount','FAllAmount','FAllAmount_LC'].includes(c[0])?'取自明细表':''); return `${c[1]}`; }).join('')+''; body.innerHTML=rows.map((row,index)=>{const missing=erpKingdeeMissingFields(row.data),state=row.customer_skip?'不核对':missing.length?`字段不完整`:'字段完整';return `${(r.page-1)*r.page_size+index+1}${state}${erpKingdeeColumns.map(c=>`${esc(erpApiCellValue(row.data,c[0],c[2]))}`).join('')}`;}).join('')||`
暂无原始数据
`; renderErpKingdeeTotals(foot,r.totals||{},r.total||0); }else if(kingdeeDocumentSources.has(source)){ const keys=(r.field_keys||[]).length?r.field_keys:[...new Set(rows.flatMap(row=>Object.keys(row.data||{})))],labels=r.field_labels||{}; head.innerHTML='序号数据状态'+keys.map(key=>`${esc(labels[key]||key)}`).join('')+''; body.innerHTML=rows.map((row,index)=>{const state=row.record_status==='invalid'?`字段不完整`:'字段完整';return `${(r.page-1)*r.page_size+index+1}${state}${keys.map(key=>`${esc(erpApiCellValue(row.data,key,key==='FDate'||key==='FDATE'?'date':''))}`).join('')}`;}).join('')||`
暂无原始数据
`; if(foot)foot.innerHTML='合计(全部 '+(r.total||0)+' 条)'+keys.map(key=>`${Object.prototype.hasOwnProperty.call(r.totals||{},key)?esc(erpKingdeeTotalValue(key,r.totals[key])):''}`).join('')+''; }else{ if(foot)foot.innerHTML=''; const keys=source==='jiaplus_self_ship'? ['sourceOrderNo','foOrderNo','shopName','sourceChannel','accessMode','foOrderStatus','orderTypeName','deliveryMethod','warehouseName','warehouseRefCode','deliveryTime','orderTime','paymentTime','sellerShipmentEndTime','createTime','currency','orderTotalAmountExcludingTax','discount','buyerPayShipFee','taxAmount','points','shippingAmount']: source==='jiaplus_delivery'?erpJiaplusDeliveryFields.map(([key])=>key):[]; if(source==='jiaplus_self_ship')rows.forEach(row=>Object.keys(row.data||{}).forEach(key=>{ if(source==='jiaplus_self_ship'&&erpJiaplusSelfShipNestedFields.includes(key))return; if(!keys.includes(key)&&keys.length<30)keys.push(key); })); if(source==='jiaplus_self_ship')erpJiaplusSelfShipExtraFields.forEach(key=>{if(!keys.includes(key))keys.push(key);}); head.innerHTML='序号数据状态外部ID'+keys.map(key=>`${esc(source==='jiaplus_self_ship'?(erpJiaplusSelfShipFieldLabels[key]||key):source==='jiaplus_delivery'?(erpJiaplusDeliveryFieldMap[key]?.label||key):key)}`).join('')+''; body.innerHTML=rows.map((row,index)=>`${(r.page-1)*r.page_size+index+1}${erpJiaplusStatusHtml(row)}${esc(row.external_id||'')}${keys.map(key=>`${esc(erpApiCellValue(row.data,key))}`).join('')}`).join('')||'
暂无原始数据
'; } const rangeStart=r.total?(r.page-1)*r.page_size+1:0,rangeEnd=Math.min(r.page*r.page_size,r.total),pages=Math.max(1,Math.ceil(r.total/r.page_size)),batchText=source==='jiaplus_self_ship'||source==='jiaplus_delivery'?'当前预览数据':(r.batch_ids||[]).length?'合并组织数据':'当前组织数据'; document.getElementById(source+'Page').innerText='显示第 '+rangeStart+'-'+rangeEnd+' 条,共 '+r.total+' 条(第 '+r.page+'/'+pages+' 页);'+batchText; }catch(e){toast('加载原始数据失败:'+erpApiFriendlyError(e));} } function changeErpApiPage(source,delta){if(delta>0&&erpApiPages[source]*erpApiPageSizes[source]>=erpApiTotals[source])return;erpApiPages[source]=Math.max(1,erpApiPages[source]+delta);renderErpApiRecords(source);} function changeErpApiPageSize(source){erpApiPageSizes[source]=parseInt(document.getElementById(source+'PageSize').value)||100;erpApiPages[source]=1;renderErpApiRecords(source);}