kyle 3 dagar sedan
incheckning
5be549f616
26 ändrade filer med 2003 tillägg och 0 borttagningar
  1. BIN
      assets/HLXYWPA2026089-Proforma Invoice-初稿.xlsx
  2. BIN
      assets/HLXYWPA2026089-车辆销售合同--初稿(FCA).docx
  3. 625 0
      assets/docx_structure.json
  4. BIN
      assets/~$HLXYWPA2026089-Proforma Invoice-初稿.xlsx
  5. BIN
      assets/~$XYWPA2026089-车辆销售合同--初稿(FCA).docx
  6. BIN
      assets/~$单一车型价格表+配置 .xlsx
  7. BIN
      assets/单一车型价格表+配置 .xlsx
  8. 21 0
      assets/订单合同信息案例.txt
  9. BIN
      auto-generate-export-contracts.skill
  10. 119 0
      auto-generate-export-contracts/SKILL.md
  11. BIN
      auto-generate-export-contracts/assets/proforma-invoice-template.xlsx
  12. BIN
      auto-generate-export-contracts/assets/vehicle-price-config.xlsx
  13. BIN
      auto-generate-export-contracts/assets/vehicle-sales-contract-template.docx
  14. 45 0
      auto-generate-export-contracts/references/field-mapping.md
  15. BIN
      auto-generate-export-contracts/scripts/__pycache__/match_vehicle.cpython-313.pyc
  16. BIN
      auto-generate-export-contracts/scripts/__pycache__/number_to_words.cpython-313.pyc
  17. BIN
      auto-generate-export-contracts/scripts/__pycache__/parse_order_info.cpython-313.pyc
  18. 418 0
      auto-generate-export-contracts/scripts/generate_contracts.py
  19. 160 0
      auto-generate-export-contracts/scripts/match_vehicle.py
  20. 66 0
      auto-generate-export-contracts/scripts/number_to_words.py
  21. 258 0
      auto-generate-export-contracts/scripts/parse_order_info.py
  22. BIN
      auto-generate-export-contracts/test_output/HLXYWPA20260530-Proforma Invoice.xlsx
  23. BIN
      auto-generate-export-contracts/test_output/HLXYWPA20260530-车辆销售合同.docx
  24. BIN
      auto-generate-export-contracts/test_output/~$HLXYWPA20260530-Proforma Invoice.xlsx
  25. BIN
      auto-generate-export-contracts/test_output/~$XYWPA20260530-车辆销售合同.docx
  26. 291 0
      skill-development-plan.md

BIN
assets/HLXYWPA2026089-Proforma Invoice-初稿.xlsx


BIN
assets/HLXYWPA2026089-车辆销售合同--初稿(FCA).docx


+ 625 - 0
assets/docx_structure.json

@@ -0,0 +1,625 @@
+{
+  "paragraphs": [
+    {
+      "index": 0,
+      "text": "Vehicle Sales Contract"
+    },
+    {
+      "index": 1,
+      "text": "车辆销售合同"
+    },
+    {
+      "index": 2,
+      "text": "No.:"
+    },
+    {
+      "index": 3,
+      "text": "签署日期Signature Date:"
+    },
+    {
+      "index": 5,
+      "text": "Seller: GUANGXI HAOLING AUTOMOTIVE TECHNOLOGY COMPANY LIMITED"
+    },
+    {
+      "index": 6,
+      "text": "卖方:广西昊菱汽车科技有限公司"
+    },
+    {
+      "index": 7,
+      "text": "ADD.:No. 18th Hexi Road, Liuzhou, Guangxi, China"
+    },
+    {
+      "index": 8,
+      "text": "地址:中国广西柳州市河西路18号"
+    },
+    {
+      "index": 9,
+      "text": "Tel电话: 0086-772-2658310"
+    },
+    {
+      "index": 11,
+      "text": "Buyer:"
+    },
+    {
+      "index": 12,
+      "text": "买方:"
+    },
+    {
+      "index": 13,
+      "text": "ADD :"
+    },
+    {
+      "index": 14,
+      "text": "地址:"
+    },
+    {
+      "index": 15,
+      "text": "Tel电话:"
+    },
+    {
+      "index": 17,
+      "text": "This Sales Contract (“Contract”) is made by and between the Seller and the Buyer above named and the Buyer agrees to buy and Seller agrees to sell the under mentioned goods according to the following terms and conditions:"
+    },
+    {
+      "index": 18,
+      "text": "买卖双方同意签订本销售合同(“合同”),根据如下条款由卖方出售,买方购进如下货物:"
+    },
+    {
+      "index": 20,
+      "text": "Commodity(hereinafter as of the “Vehicles”)商品(下称“车辆”):"
+    },
+    {
+      "index": 22,
+      "text": "Export Destination:  (hereinafter as of “Territory”)"
+    },
+    {
+      "index": 23,
+      "text": "出口目的地:(下称“区域”)"
+    },
+    {
+      "index": 24,
+      "text": "The Vehicles under the Contract shall not be exported to other countries except the country agreed upon by this clause, nor shall be sold domestically in China."
+    },
+    {
+      "index": 25,
+      "text": "本合同项下的车辆不得出口到本条规定的国家外,也不得转中国国内销售。"
+    },
+    {
+      "index": 26,
+      "text": "The Seller will not support any process and documents related to homologation and registration, and the above quotation does not include homologation and registration fee."
+    },
+    {
+      "index": 27,
+      "text": "卖方将不会支持任何认证和公告的流程和文件资料,同时此次报价不包含认证和公告费。"
+    },
+    {
+      "index": 28,
+      "text": "Incoterms: FCA Port in China贸易条款:FCA 港口"
+    },
+    {
+      "index": 29,
+      "text": "The color of Product: To be cover by the Buyer. 车辆颜色: 买方决定。"
+    },
+    {
+      "index": 30,
+      "text": "Packing: Bare. 包装:裸装。"
+    },
+    {
+      "index": 31,
+      "text": "Shipping marks: N/M. 运输标记: 无。"
+    },
+    {
+      "index": 32,
+      "text": "Partial Delivery: Allowed.  允许分批发运。"
+    },
+    {
+      "index": 33,
+      "text": "Cost , Risks and Title   费用,风险及车辆所有权交割"
+    },
+    {
+      "index": 34,
+      "text": "Unless otherwise agreed between the Parties, Seller will ship the Vehicles in accordance with the shipping terms as below in accordance with INCOTERM 2020 -FCA."
+    },
+    {
+      "index": 35,
+      "text": "除非双方另行约定,卖方将根据下述依据INCOTERM 2020-FCA条款的规定运输方式运输车辆。"
+    },
+    {
+      "index": 37,
+      "text": "The risk of any loss, damage or deterioration of such Vehicles will be borne by Distributor from the time Vehicles have been delivered by Seller to the carrier.  Title in such Vehicles shall pass from Seller to Buyer when Vehicles have been delivered by Seller to and accepted by the carrier."
+    },
+    {
+      "index": 38,
+      "text": "在卖方将车辆交付给承运商的一刻之后,任何车辆的丢失、损坏或变质风险将由买方承担。同时,车辆的所有权也在卖方将车辆移交给承运商后由卖方转移到买方。"
+    },
+    {
+      "index": 39,
+      "text": "Terms of Payment付款方式"
+    },
+    {
+      "index": 40,
+      "text": "30% of the Total Amount shall be paid to the Seller by T/T before the production. 70% of the Total Amount shall be paid to the Seller by the Buyer by means of T/T before the delivery of vehicles."
+    },
+    {
+      "index": 41,
+      "text": "生产前,买方向卖方通过电汇支付本合同项下30% 货款。发运前,买方向卖方通过电汇支付本合同项下70% 货款。"
+    },
+    {
+      "index": 42,
+      "text": "2) All payments shall be made by wire transfer to the following bank account or any other bank account designated by Seller from time to time:"
+    },
+    {
+      "index": 43,
+      "text": "所有付款均应电汇至以下银行账户或卖方不时指定的任何其他的银行账户:"
+    },
+    {
+      "index": 46,
+      "text": "3) All bank charges shall be borne by the Buyer except the bank charges imposed by the receiving bank designated in Clause 9(2).. Buyer shall pay the bank handling fees at the same time as paying for payment to ensure that the full amount is received in Seller's designated receiving account and cannot be deducted or offset for any reason. If Buyer is indeed unable to pay the intermediary bank's handling fees, Buyer shall explain the situation to Seller, Seller can exempt Buyer from paying the handling fee according to such situation. Seller's notification of exemption includes but is not limited to email, letter, etc.."
+    },
+    {
+      "index": 47,
+      "text": "除9(1)条款中指定的收款银行以外的所有银行费用由买方承担。买方须在付款时足额预留银行手续费,确保款项全额到达卖方指定收款账户,不得以任何理由扣减或抵消。如买方确实无法支付中间行的手续费,买方应向卖方解释情况,卖方依据情况可豁免买方支付该手续费的义务,卖方通知豁免的形式包括但不限于邮件、信函等。"
+    },
+    {
+      "index": 48,
+      "text": "When the Buyer paid with USD, the Seller and the Buyer have the right to claim the difference based on the central parity of USD/CNY exchange rate of the PBOC on the Buyer’s payment date if the fluctuation range between the central parity of USD/CNY exchange rate of PBOC on the Buyer’s payment date and the Seller’s invoice date is equal to or more than 2%."
+    },
+    {
+      "index": 49,
+      "text": "如果买方使用美元付款时,付款日的中国人民银行美元兑人民币汇率中间价与发票日的中国人民银行美元兑人民币汇率中间价相比,波动幅度超过2%(含2%),卖方和买方有权按买方付款日的中国人民银行美元兑人民币汇率中间价要求差价索赔。"
+    },
+    {
+      "index": 50,
+      "text": "Shipment装运"
+    },
+    {
+      "index": 51,
+      "text": "The Seller will arrange production within 30 days after 30% T/T is received, and the actual production cycle depends on the availability of materials. After the production is completed, the sample vehicles will be delivered to the port designated by the Buyer. If the Buyer does not accept the delivery of the Vehicles within 3 months after the confirmation of its order and contract, the order shall be considered as cancellation. The Seller shall have the rights to resell the order in other markets or make other handling after fully considering the opinion of the Buyer. After deciding the final solution, the Buyer shall be obligated to bear the charge related with reselling or handling according to the standard rate per vehicle."
+    },
+    {
+      "index": 52,
+      "text": "卖方收到30%的货款后30天内安排生产,实际生产周期取决于物料达到情况。生产完毕后按买方指定港口交货。若买方在确认订单和合同后3个月未接受装运汽车则视为取消订单,卖方有权对该订单在充分考虑买方表达意见后,在其他市场转销或作其他处理,在决定最终处理方案后,买方应有义务按照每辆车的标准费用承担与转销或处理相关的费用。"
+    },
+    {
+      "index": 53,
+      "text": "Shipping Documents装船单据:"
+    },
+    {
+      "index": 54,
+      "text": "1) Commercial Invoice (Original, Company Stamp & Signed) - 3 Copies一式三份的商业发票"
+    },
+    {
+      "index": 55,
+      "text": "2) Packing List (Original, Company Stamp & Signed) - 3 Copies一式三份装箱单"
+    },
+    {
+      "index": 56,
+      "text": "3) Certificate of Origin - (original and duplicate copy) - if needed 原产地证(正本和副本)- 如需"
+    },
+    {
+      "index": 57,
+      "text": "Licenses, Duties and taxes许可证、税收:"
+    },
+    {
+      "index": 58,
+      "text": "Except as otherwise provided herein, all import permits and license and the import duties, customs fees and all taxes levied by any government authority other than the Sellers country be the sole responsibility of the Buyer."
+    },
+    {
+      "index": 59,
+      "text": "除非另有特别规定,所有进口许可证和进口关税、海关费用和所有非卖方所在国政府征收的税费将由买方承担。"
+    },
+    {
+      "index": 60,
+      "text": "After-sales Service 售后服务"
+    },
+    {
+      "index": 61,
+      "text": "The Buyer is fully responsible for the repair and maintenance of the Vehicles."
+    },
+    {
+      "index": 62,
+      "text": "车辆的维修、保养由买方全权负责。"
+    },
+    {
+      "index": 63,
+      "text": "The Buyer is well aware of the product characteristics of vehicles and has qualified facilities and capability for the repair and maintenance of the Vehicles."
+    },
+    {
+      "index": 64,
+      "text": "买方已知悉汽车的产品特性,且具备合格的设施和能力对车辆进行维修和保养。"
+    },
+    {
+      "index": 65,
+      "text": "Except for the claim processing mentioned in Clause 17, all other risks and responsibilities shall be borne by the Buyer."
+    },
+    {
+      "index": 66,
+      "text": "除本17条项下所提及的索赔处理外,其他所有风险和责任均由买方自行承担。"
+    },
+    {
+      "index": 67,
+      "text": "Intellectual Property Rights 知识产权"
+    },
+    {
+      "index": 68,
+      "text": "14.1   No Transferring of Intellectual Property Rights 知识产权不可转移"
+    },
+    {
+      "index": 69,
+      "text": "No provision of this Contract shall be construed to transfer to Buyer any intellectual property rights relating to Products, such as Trademarks, copyrights, patents and utility models."
+    },
+    {
+      "index": 70,
+      "text": "本合同中的任何条款都不应被理解为将与产品相关的知识产权,如商标、版权、专利和实用新型等转让给买方。"
+    },
+    {
+      "index": 71,
+      "text": "14.2  Control and Monitor of Intellectual Property Rights 知识产权控制和监控"
+    },
+    {
+      "index": 72,
+      "text": "Buyer shall constantly make its best effort to control the intellectual property rights on Products not infringed by a third party in the Territory, and shall accurately and promptly inform Seller in case the intellectual property rights could be infringed by any third party so to allow Seller’s appropriate intervention, for this purpose, and while necessary, Buyer shall have the responsibility and obligation to assist Seller to take necessary actions."
+    },
+    {
+      "index": 73,
+      "text": "买方必须不断尽全力去控制产品的知识产权不被区域内的任何第三方侵犯,如果产品的知识产权被第三方侵犯,买方必须准确、及时地通知卖方以便卖方采取适当的干涉,为此目的,如果有必要,买方须有义务和责任协助卖方采取必要的行动。"
+    },
+    {
+      "index": 74,
+      "text": "Use of Trademarks and Trade Name商标和商号的使用"
+    },
+    {
+      "index": 75,
+      "text": "15.1 “Trademarks” means such trademarks, trade names, words, signs, designs and service marks as"
+    },
+    {
+      "index": 76,
+      "text": "listed in the Schedule A attached to this Contract."
+    },
+    {
+      "index": 77,
+      "text": "“商标”指本协议附件A内明确的商标、商号、文字、标志、设计和服务商标。"
+    },
+    {
+      "index": 78,
+      "text": "15.2   Acknowledgment of Intellectual Property Rights知识产权的认识"
+    },
+    {
+      "index": 79,
+      "text": "Buyer will not take or assist any other person to take any action that may invalidate, prejudice or impair any right, title and interest of Seller in and to such Trademarks and trade names."
+    },
+    {
+      "index": 80,
+      "text": "买方不会采取,也不会协助任何其他人采取可能使卖方对该等商标和商号的任何权利、所有权和利益无效,被损害或被侵害的任何行动。"
+    },
+    {
+      "index": 81,
+      "text": "15.3   Restrictions of Registration 限制注册"
+    },
+    {
+      "index": 82,
+      "text": "Buyer shall not, either during the term of this Contract or after its termination or expiration of this Contract attempt to register, record, use or authorize the use of any trademarks or trade names or other distinctive marks, names, words, signs or designs owned by Seller, including Trademarks, or any mark, name, word or sign which so nearly resembles any of the foregoing marks, names, words, signs or designs as to be likely to cause confusion or mistake or to deceive the public, or any translation or transliteration of any Trademark word or sign into any language in any state, region or country."
+    },
+    {
+      "index": 83,
+      "text": "在本合同期限内或在本合同终止或期满后,买方不得试图注册、备案、使用或授权使用卖方拥有的任何商标、商号或其它独特标志、名称、文字、标记或设计,包括商标,或与上述标志、名称、文字、标记或设计相似到可能引起公众混淆、错误或欺骗公众的任何标志、名称、文字或标记,或任何商标文字或标记在任何州、地区或国家的任何语种的翻译或音译。"
+    },
+    {
+      "index": 84,
+      "text": "15.4   Restrictions on Use限制使用"
+    },
+    {
+      "index": 85,
+      "text": "Without prior written consent of Seller, Buyer shall not change the Trademarks of Products, and the Trademarks shall not be used on or in connection with any non-Product or services."
+    },
+    {
+      "index": 86,
+      "text": "未经卖方事先书面同意,买方不得擅自更改车辆的商标,并且商标不得用于或联系于非产品或服务上。"
+    },
+    {
+      "index": 87,
+      "text": "15.5   Buyer and his agency or authorized dealers shall use or display the Trademarks only in the"
+    },
+    {
+      "index": 88,
+      "text": "forms, color, dimension or manner approved by Seller, which may be amended from time-to-time during the term of this Contract, in Seller’s sole discretion."
+    },
+    {
+      "index": 89,
+      "text": "买方和其代理或授权经销商应仅按卖方批准的形式、颜色、尺寸或方式使用或展示商标,本合同期限内卖方可自主决定不时变更该等形式、颜色、尺寸或方式。"
+    },
+    {
+      "index": 90,
+      "text": "15.6   Comply with All Applicable Laws遵守所有适用的法律"
+    },
+    {
+      "index": 91,
+      "text": "Buyer shall ensure that all advertising and promotional literature containing the Trademarks and all the use of the Trademarks shall strictly comply with all applicable laws and regulations in force in the Territory."
+    },
+    {
+      "index": 92,
+      "text": "买方应保证所有包含商标的广告和促销刊物以及对商标的所有使用应严格遵守区域内所有适用的现行的法律法规。"
+    },
+    {
+      "index": 93,
+      "text": "15.7   Use by Third Parties第三方使用"
+    },
+    {
+      "index": 94,
+      "text": "Buyer may not permit the use of the Trademarks within the Territory by its agency or its authorized dealers for advertising, promotional activities and materials created on its behalf."
+    },
+    {
+      "index": 95,
+      "text": "买方不可允许其代理商或其授权经销商代表买方在区域内使用商标进行广告、促销活动和材料制作。"
+    },
+    {
+      "index": 96,
+      "text": "15.8   Restrictions on Agency or Authorized Dealer Use对代理和授权经销商的使用限制"
+    },
+    {
+      "index": 97,
+      "text": "Buyer must ensure that the agency or authorized dealers:"
+    },
+    {
+      "index": 98,
+      "text": "买方须确保其代理或授权经销商:"
+    },
+    {
+      "index": 99,
+      "text": "1)Will not take or assist any other person to take any action that may invalidate, prejudice or impair any right, title and interest of Seller in and to its Trademarks;"
+    },
+    {
+      "index": 100,
+      "text": "不会采取或协助他人采取可能令卖方对该等商标和商号的任何权利、所有权和利益无效,被损害或被侵害的任何行动;"
+    },
+    {
+      "index": 101,
+      "text": "2)Will not use any other mark, name, word or sign which so nearly resembles any of Trademarks as to be likely to cause confusion or mistake or to deceive the public; and"
+    },
+    {
+      "index": 102,
+      "text": "将不使用与商标类似的可能引起公众混淆、错误或欺骗公众的任何其它标志、名称、文字或标记;及"
+    },
+    {
+      "index": 103,
+      "text": "3)Will not, during the term of their respective Contracts or after its termination or expiration, attempt to register any trademarks, trade names or other distinctive marks, names, words, signs or designs of Seller, including Trademarks, or any mark, name, word or sign which so nearly resembles any of the foregoing marks, names, signs or designs as to be likely to cause confusion or mistake or to deceive the public, or any translation or transliteration thereof into any language in any state, region or country."
+    },
+    {
+      "index": 104,
+      "text": "在各自合同期限内或在该等合同终止或期满后,不会试图注册卖方的任何商标、商号或其它独特标志、名称、文字、标记或设计,包括商标,或与上述标志、名称、文字、标记或设计相似到可能引起公众混淆、错误或欺骗公众的任何标志、名称、文字或标记,或其在任何州、地区或国家的任何语种的翻译或音译。"
+    },
+    {
+      "index": 105,
+      "text": "15.9    Monitoring and Infringement监督和侵权"
+    },
+    {
+      "index": 106,
+      "text": "Buyer and its agency and authorized dealers shall monitor the use of the Trademarks in the Territory and notify Seller immediately of any unauthorized use or counterfeiting by a third party. Seller shall have the sole right to take legal action against the third party in cases where in Seller's sole opinion, after consultation with Buyer, it is reasonable to pursue such action. Seller and Buyer shall cooperate in such legal action and Buyer shall assist Seller in obtaining evidence of such unauthorized use or counterfeiting and promptly furnish to Seller all relevant evidence which comes into its possession."
+    },
+    {
+      "index": 107,
+      "text": "买方和其代理及授权经销商应监督商标在区域内的使用,如发现第三者未经授权使用或伪造商标,应立即通知卖方。如卖方与买方进行协商后,卖方单方认为采取该等行动是合理的,卖方有权采取针对第三方的法律行动。卖方和买方应当在该等法律行动中合作,并且买方应当协助卖方获取未经授权使用或伪造的证据,并及时向卖方提供其掌握的所有有关证据。"
+    },
+    {
+      "index": 108,
+      "text": "In the event that any suit, claim, demand or other action is threatened or brought against Buyer involving that Buyer’s use of any of the Trademarks as authorized by this Contract infringes any rights of any third party, Buyer shall promptly notify Seller therefore, and Seller or its designee shall have the right, at its option to take exclusive charge of the defense at its own expense. Buyer shall cooperate fully with Seller or its designee in such defense in which Seller participates."
+    },
+    {
+      "index": 109,
+      "text": "如有涉及买方根据本合同授权使用任何商标侵犯了任何第三方权利的针对买方的任何诉讼、索赔、请求或其它行动可能发生或者已经提出,买方应及时通知卖方。卖方或其委派人员应有权自行选择自费全权负责进行辩护。在卖方参与的该等辩护中,买方应与卖方或其委派人员充分合作。"
+    },
+    {
+      "index": 110,
+      "text": "15.10   Discontinue of Use停止使用"
+    },
+    {
+      "index": 111,
+      "text": "Upon expiration or termination of this Contract, Buyer shall assure itself, its agency and authorized dealer returning to Seller all materials in its control or possession bearing the Trademarks within 90 calendar days after the expiration or termination of this Contract;"
+    },
+    {
+      "index": 112,
+      "text": "本合同期满或终止后,买方应确保其自身、其代理和其授权经销商须在本合同期满或终止后90个日历日内向卖方归还由其控制或占有的附有商标的所有材料。"
+    },
+    {
+      "index": 113,
+      "text": "The contract is terminated three months after the arrival of the goods at the port of destination."
+    },
+    {
+      "index": 114,
+      "text": "本合同在货物到达目的港三个月后终止。"
+    },
+    {
+      "index": 115,
+      "text": "Force Majeure不可抗力:"
+    },
+    {
+      "index": 116,
+      "text": "The Seller shall not be responsible for failure or delay in delivery of all or any part of the Contract goods in any case of force majeure including war, strike, fire, flood, earthquake, typhoon, natural catastrophe and any other contingencies which prevent shipment within the stipulated period. However, the Seller shall immediately notify the Buyer by cable or telex or fax and afterwards at earliest possibility date furnish the letter with a certificate issued by the China Council for the Promotion of International Trade or other related organizations."
+    },
+    {
+      "index": 117,
+      "text": "如出现不可抗力,如战争、罢工、火灾、洪水、地震、台风、自然灾害和任何其他妨碍在约定日期内装船的或有事件,卖方将不承担由此引起的迟延或无法交货责任。然而卖方应该立即通过电话、电传或传真通知买方,随后在最短时间内提供一份信函,并附上由中国国际贸易促进委员会或其他相关组织签署的证明。"
+    },
+    {
+      "index": 118,
+      "text": "Pre-Delivery Inspection and defects claim handling 交付前检查及瑕疵索赔处理"
+    },
+    {
+      "index": 119,
+      "text": "17.1 (a) The Parties agree that after the arrival of the Vehicles (along with any Parts shipped together (if any)) to the Territory, buyer has the right to conduct, at its own cost, a pre-delivery inspection (“PDI”) for such Vehicles and Parts (if any) and will inform seller, in writing, of any finding at PDI no later than 30 working days after the arrival of such Vehicles;"
+    },
+    {
+      "index": 120,
+      "text": "各方同意在汽车(与一起装运的零件(如有))抵达区域后,买方有权对该些汽车及零件(如有)自费进行装运前检查(“PDI”)并在不晚于该些汽车到达后的30个工作日内,将PDI的任何发现书面通知卖方;"
+    },
+    {
+      "index": 121,
+      "text": "(b) Seller shall indemnify buyer against the invoiced value of any shortage, defective or incorrect Parts or Vehicles found at PDI after confirmation. Unless otherwise agreed by the Parties or explicitly specified in this Contract, other than the indemnification described in this paragraph, SELLER will not undertake any other costs associated with the event of shipment discrepancy described in this paragraph;"
+    },
+    {
+      "index": 122,
+      "text": "卖方应向买方补偿经确认的在PDI中所发现的漏装、有缺陷或错误的零件或汽车的发票价值。除非各方另行书面同意或本合同另有明确规定,除了本段所述的补偿外,卖方将不再承担与本段所述的发货差异相关的其他费用。"
+    },
+    {
+      "index": 123,
+      "text": "(c) Each Party shall provide other Parties with all reasonable assistance with any claim the other Parties wish to make against a carrier or other logistics provider in relation to any damage or loss to the Vehicles or Parts."
+    },
+    {
+      "index": 124,
+      "text": "各方对于其他方希望可以就任何损坏或丢失的汽车或零件向承运人或其他物流提供者提出的索赔,其应向其他方提供一切合理协助。"
+    },
+    {
+      "index": 125,
+      "text": "(d) Buyer shall mitigate any costs which may be subject to indemnification by seller under this Section 17 to the greatest extent possible."
+    },
+    {
+      "index": 126,
+      "text": "对于卖方就本17条项下需给予买方免责的费用,买方应最大程度地降低该些费用。"
+    },
+    {
+      "index": 127,
+      "text": "17.2 All losses or damages of the commodity due to the buyer's inland transport, handling, non proper store or any other cause, shall be settled by the buyer at his own expense."
+    },
+    {
+      "index": 128,
+      "text": "由于买方内陆运输、操作、不适当的储存或任何其他原因引起的商品损失或毁坏应该由买方自费解决。"
+    },
+    {
+      "index": 129,
+      "text": "Export Controls&Anti-Bribery & Anti-Corruption出口管制、反贿赂与反腐败:"
+    },
+    {
+      "index": 130,
+      "text": "The Parties agree to comply with all applicable export control and sanctions laws and regulations, including those of China, Export Destination, the United States, and any other relevant country (the “Export Control Laws”). The Parties agree to comply with all applicable China, Export Destination and the United States anti-corruption laws"
+    },
+    {
+      "index": 131,
+      "text": "双方同意遵守所有适用的出口管制和制裁法律及规定,包括中国、出口目的地、美国,以及其他相关的国家(“出口管制法律”)。双方同意遵守所有适用的中国、出口目的地和美国的反腐败法律。"
+    },
+    {
+      "index": 133,
+      "text": "Arbitration仲裁:"
+    },
+    {
+      "index": 134,
+      "text": "Any disputes arising from, out of, or in connection with this contract shall be settled through friendly consultations between the Parties. Such consultations shall begin immediately after one Party has delivered to the other Party a notice requesting such consultation. If within thirty (30) days following the date on which such notice is given the dispute cannot be settled through consultations, either party may refer the dispute to arbitration in China International Economic and Trade Arbitration Commission for resolution in accordance with the rules of arbitration then in force of the above commission. The Chinese language shall be the only language of any arbitration proceedings. The arbitration decision will be final and binding on both parties,not subject to any appeal, and costs of arbitration will be paid by loser."
+    },
+    {
+      "index": 135,
+      "text": "因本合同产生的、或与本合同相关的任何争议双方应通过友好协商解决。该等协商应在一方向另一方发出要求协商的通知后立即开始。若在该等通知发出后的三十(30)日内,争议不能通过协商解决,任何一方可将该争议提交至中国国际经济贸易仲裁委员会仲裁,并按按该会届时有效的仲裁规则仲裁解决。中文为任何仲裁程序的唯一语言。仲裁裁决为终局,并对双方有约束力并且不得上诉。仲裁败诉一方支付所有仲裁费用。"
+    },
+    {
+      "index": 136,
+      "text": "This Contract shall be governed and construed according to the laws of People’s Republic of China."
+    },
+    {
+      "index": 137,
+      "text": "本协议应受中国法律管辖并根据中国法律解释。"
+    },
+    {
+      "index": 138,
+      "text": "This Contract is made in two originals, one for each party. The Contract may be executed and delivered by facsimile and email. This contract is executed in Chinese and English language. In the event of any inconsistency, the Chinese version shall prevail."
+    },
+    {
+      "index": 139,
+      "text": "该合同一式两份,双方各持一份。本合同以中文和英文起草和签署和递送。传真签署或电邮签署有效。如有歧义,以中文版本为准。"
+    },
+    {
+      "index": 142,
+      "text": "Seller: GUANGXI HAOLING AUTOMOTIVE TECHNOLOGY COMPANY LIMITED"
+    },
+    {
+      "index": 143,
+      "text": "卖方:广西昊菱汽车科技有限公司"
+    },
+    {
+      "index": 146,
+      "text": "Signature 签字:"
+    },
+    {
+      "index": 147,
+      "text": "Name姓名:"
+    },
+    {
+      "index": 148,
+      "text": "Title职位:"
+    },
+    {
+      "index": 155,
+      "text": "Signature 签字:"
+    },
+    {
+      "index": 156,
+      "text": "Name姓名:"
+    },
+    {
+      "index": 157,
+      "text": "Title职位:"
+    },
+    {
+      "index": 160,
+      "text": "Schedule A: Trademarks List"
+    },
+    {
+      "index": 161,
+      "text": "附件A:商标列表"
+    }
+  ],
+  "tables": [
+    {
+      "index": 0,
+      "rows": [
+        {
+          "index": 0,
+          "cells": [
+            "Description\n货物描述",
+            "Quantity\n数量\n(Units)",
+            "Unit Price (USD)\n单价\n(美元)",
+            "Amount\n (USD)\n总金额\n(美元)"
+          ]
+        },
+        {
+          "index": 1,
+          "cells": [
+            "Model: ,  \n\nColor: 1\n型号:,  \n\n颜色:1",
+            "",
+            "",
+            ""
+          ]
+        },
+        {
+          "index": 8,
+          "cells": [
+            "Total:   units  总数量:  2 辆    \nTotal Amount:  USD .00  总金额: 美元 .00\n (Say USD:  ONLY)\n(合计美元:美元整)\nFCA  Port in China    FCA  中国港口",
+            "Total:   units  总数量:  2 辆    \nTotal Amount:  USD .00  总金额: 美元 .00\n (Say USD:  ONLY)\n(合计美元:美元整)\nFCA  Port in China    FCA  中国港口",
+            "Total:   units  总数量:  2 辆    \nTotal Amount:  USD .00  总金额: 美元 .00\n (Say USD:  ONLY)\n(合计美元:美元整)\nFCA  Port in China    FCA  中国港口",
+            "Total:   units  总数量:  2 辆    \nTotal Amount:  USD .00  总金额: 美元 .00\n (Say USD:  ONLY)\n(合计美元:美元整)\nFCA  Port in China    FCA  中国港口"
+          ]
+        }
+      ]
+    },
+    {
+      "index": 1,
+      "rows": [
+        {
+          "index": 0,
+          "cells": [
+            "Name of Beneficiary: GUANGXI HAOLING AUTOMOTIVE TECHNOLOGY COMPANY LIMITED\nAddress of Beneficiary: THE 3RD FLOOR OF ADMINISTRATION BUILDING,NO.18 HEXI ROAD,LIUZHOU,GUANGXI,P.R.CHINA\nAccount Number: 623685393892(CNY account)\n               623685394818(USD account)\nName of Beneficiary’s Bank: BANK OF CHINA LTD.,LIUZHOU BRANCH\nAddress: NO.178 Pingshan Ave., Liuzhou Guangxi P. R. China\nSWIFT CODE: BKCHCNBJ49D"
+          ]
+        }
+      ]
+    },
+    {
+      "index": 2,
+      "rows": [
+        {
+          "index": 0,
+          "cells": [
+            "Trade Name\n\n\n\n\nWULING",
+            "Trademarks"
+          ]
+        }
+      ]
+    }
+  ]
+}

BIN
assets/~$HLXYWPA2026089-Proforma Invoice-初稿.xlsx


BIN
assets/~$XYWPA2026089-车辆销售合同--初稿(FCA).docx


BIN
assets/~$单一车型价格表+配置 .xlsx


BIN
assets/单一车型价格表+配置 .xlsx


+ 21 - 0
assets/订单合同信息案例.txt

@@ -0,0 +1,21 @@
+1. 姓名:陈凯武;
+2.任职:经理;
+3.意向车型:                 
+(1) 荣光新双排货车  N350 Double Cab Pickup,- 型号:LZW1028SPY
+国V China Ⅴ  代码:G33N    1台银色
+(2)五菱荣光新卡双后轮 2.0L 5MT 单排 型号:LZW5030XXYLGHUG
+国六B(RDE)  代码:AGMC  1台银色
+4.合同签约公司中文名称:财货通(香港)国际供应链有限公司;
+5.合同签约公司英文名称:Finmo (HongKong)International supply chain Co.,Ltd.
+6.合同签约公司中文地址:香港德輔道西 93-97 號聯威商業大廈 13 樓 B-C 室
+7.合同签约公司英文地址:FLAT B&C,13/F.,LUEN WAI COMMERCIAL BUILDING,93-97 DES VOEUX ROAD WEST,HONG KONG
+8.合同联系电话:13640613242
+9.国内出发港口:广州南沙;
+10.目的地国家:巴拿马Panama
+11.目的地港口:巴尔博亚港 Balboa
+12.结算方式:FCA
+二、付款公司:
+1.开户行名称:创兴银行
+2.开户账户:256801951225
+3.公司联系 电话:+8613640613242
+4.美元结算。

BIN
auto-generate-export-contracts.skill


+ 119 - 0
auto-generate-export-contracts/SKILL.md

@@ -0,0 +1,119 @@
+---
+name: auto-generate-export-contracts
+description: 根据订单合同信息自动生成车辆出口销售合同(Word)和Proforma Invoice(Excel)。用于处理海外车辆出口业务,支持从订单文本解析买方信息、车型列表、贸易条款等,自动匹配车型价格配置表,生成标准格式的中英双语销售合同和PI表格。当用户提供订单信息、要求生成销售合同/PI/Proforma Invoice时触发。
+---
+
+# 海外合同自动生成 Skill
+
+## 功能
+
+根据用户提供的订单合同信息,自动生成:
+1. **车辆销售合同** (Vehicle Sales Contract) - `.docx` 格式
+2. **Proforma Invoice** - `.xlsx` 格式
+
+## 触发条件
+
+当用户:
+- 提供订单信息并要求生成销售合同/PI/Proforma Invoice
+- 提供包含车型、买方公司、贸易条款等信息的订单文本
+
+## 工作流程
+
+### Step 1: 接收订单信息
+
+支持两种输入格式:
+- **结构化编号列表**(标准格式):
+  ```
+  1. 姓名:xxx;
+  2.任职:xxx;
+  3.意向车型:
+  (1) 车型中文名  英文名 - 型号:LZWxxxx
+  排放标准  代码:xxx  X台颜色
+  4.合同签约公司中文名称:xxx;
+  5.合同签约公司英文名称:xxx
+  ...
+  ```
+- **自然语言描述**:用户自由描述订单内容
+
+解析提取核心字段:
+- 买方公司中英文名称和地址
+- 合同联系电话
+- 车型列表(型号、发动机代码、数量、颜色)
+- 贸易条款(FCA/FOB/EXW)
+- 出发港口、目的地国家
+
+### Step 2: 车型价格匹配
+
+遍历 `assets/vehicle-price-config.xlsx` 中的所有sheet,在**G列(型号)**精确匹配ModelCode:
+- 匹配成功 → 根据贸易条款获取对应USD价格(FCA→O列, FOB→N列, EXW→P列)
+- 匹配失败 → **提示用户提供该车型的单价USD**
+
+**用户提供的价格优先于价格表。**
+
+### Step 3: 计算汇总
+
+- 总数量 = Σ 各车型数量
+- 总金额 = Σ (单价 × 数量)
+- 30%金额 = `ceil(总金额 × 0.30)`(向上取整)
+- 70%金额 = 总金额 - 30%金额
+- 英文大写 = `num2words(总金额).upper()`
+
+### Step 4: 生成合同编号
+
+格式:`HLXYWPA{年月日}`,如 `HLXYWPA20260530`
+
+### Step 5: 生成文件
+
+**Proforma Invoice** (`{合同编号}-Proforma Invoice.xlsx`):
+- 复制模板 `assets/proforma-invoice-template.xlsx`
+- 替换买方信息、合同编号、日期、车型明细、价格、汇总信息
+- 保留Excel公式(SUM、乘法公式)
+
+**销售合同** (`{合同编号}-车辆销售合同.docx`):
+- 复制模板 `assets/vehicle-sales-contract-template.docx`
+- 替换段落文本(合同编号、日期、买方信息、贸易条款等)
+- 替换表格(车辆明细表、汇总信息)
+- 银行账户信息保持模板固定值
+
+### Step 6: 输出
+
+向用户输出两个文件,并附加固定提示语:
+> 销售合同、Proforma Invoice已生成,报价员核对无误后再发送给客户。
+
+## 脚本使用
+
+```bash
+python scripts/generate_contracts.py <订单信息文件.txt> -o <输出目录> -p '{"LZW1028SPY": 6176}'
+```
+
+参数:
+- `order_file`: 订单信息文本文件路径
+- `-o, --output`: 输出目录(默认当前目录)
+- `-p, --prices`: JSON字符串,手动提供缺失的车型价格
+
+Python API:
+```python
+from scripts.generate_contracts import generate_contracts
+
+result = generate_contracts(order_text, output_dir='.', user_prices={"LZW1028SPY": 6176})
+# result['success'] = True/False
+# result['pi_path'] = PI文件路径
+# result['contract_path'] = 销售合同文件路径
+# result['missing_prices'] = 未匹配到价格的车型列表
+```
+
+## 依赖
+
+- `python-docx`
+- `openpyxl`
+- `num2words`
+
+## 模板文件
+
+- `assets/proforma-invoice-template.xlsx`: Proforma Invoice模板
+- `assets/vehicle-sales-contract-template.docx`: 销售合同模板
+- `assets/vehicle-price-config.xlsx`: 车型价格配置表(多sheet)
+
+## 字段映射详情
+
+详见 [references/field-mapping.md](references/field-mapping.md)

BIN
auto-generate-export-contracts/assets/proforma-invoice-template.xlsx


BIN
auto-generate-export-contracts/assets/vehicle-price-config.xlsx


BIN
auto-generate-export-contracts/assets/vehicle-sales-contract-template.docx


+ 45 - 0
auto-generate-export-contracts/references/field-mapping.md

@@ -0,0 +1,45 @@
+# 字段映射参考
+
+## 订单信息 → 合同字段映射
+
+### 买方信息
+| 订单字段 | PI位置 | 销售合同位置 |
+|---|---|---|
+| 合同签约公司英文名称 | B7 (TO:) | P11 (Buyer:) |
+| 合同签约公司中文名称 | — | P12 (买方:) |
+| 合同签约公司英文地址 | B8 (ADD:) | P13 (ADD:) |
+| 合同签约公司中文地址 | — | P14 (地址:) |
+| 合同联系电话 | B9 (Tel:) | P15 (Tel:) |
+
+### 物流信息
+| 订单字段 | PI位置 | 销售合同位置 |
+|---|---|---|
+| 国内出发港口 | E23 FCA港口 / B27 Delivery terms | P28 Incoterms |
+| 目的地国家 | — | P22 Export Destination |
+| 贸易条款 | E23 / B27 前缀 | P28 前缀 |
+| 运输标记 | G12 (固定N/M) | — |
+
+### 车辆信息
+| 订单字段 | PI位置 | 销售合同位置 |
+|---|---|---|
+| 型号 (ModelCode) | B17-B22 描述 | Table0 Row1+ 车型描述 |
+| 发动机代码 | B17-B22 描述 | Table0 Row1+ 车型描述 |
+| 数量 | C17-C22 | Table0 Row1+ 数量列 |
+| 单价USD | F17-F22 (查表或用户提供) | Table0 Row1+ 单价列 |
+| 颜色 | — | Table0 Color字段 |
+
+### 汇总信息
+| 计算项 | PI位置 | 销售合同位置 |
+|---|---|---|
+| 总数量 | H23 SUM公式 | Table0 汇总行 |
+| 总金额 | H23 SUM公式 | Table0 汇总行 |
+| 英文大写 | B24 | Table0 汇总行 |
+| 30%金额 | B29 Payment terms | — |
+| 70%金额 | B29 Payment terms | — |
+
+### 固定信息(不替换)
+| 信息 | 位置 |
+|---|---|
+| 卖方信息 | PI B1-B3 / 合同 P5-P9 |
+| 收款银行账户 | PI B30+ / 合同 Table1 |
+| 商标列表 | 合同 Table2 |

BIN
auto-generate-export-contracts/scripts/__pycache__/match_vehicle.cpython-313.pyc


BIN
auto-generate-export-contracts/scripts/__pycache__/number_to_words.cpython-313.pyc


BIN
auto-generate-export-contracts/scripts/__pycache__/parse_order_info.cpython-313.pyc


+ 418 - 0
auto-generate-export-contracts/scripts/generate_contracts.py

@@ -0,0 +1,418 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+主生成脚本:根据订单信息生成销售合同和Proforma Invoice
+"""
+
+import os
+import sys
+import shutil
+import re
+from datetime import datetime
+
+# 添加脚本目录到路径
+script_dir = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, script_dir)
+
+from parse_order_info import parse_order_info
+from match_vehicle import match_vehicle, build_vehicle_description
+from number_to_words import format_say_us_only, calculate_payment_split
+
+
+def get_asset_path(filename: str) -> str:
+    """获取assets目录下的文件路径"""
+    return os.path.join(script_dir, '..', 'assets', filename)
+
+
+def generate_contract_no() -> str:
+    """生成合同编号: HLXYWPA{年月日}"""
+    return f"HLXYWPA{datetime.now().strftime('%Y%m%d')}"
+
+
+def format_date() -> str:
+    """格式化当前日期"""
+    return datetime.now().strftime('%Y-%m-%d')
+
+
+def format_date_for_cell():
+    """Excel单元格日期格式(仅日期,无时间)"""
+    from datetime import datetime
+    now = datetime.now()
+    return datetime(now.year, now.month, now.day)
+
+
+def translate_port(port_cn: str) -> str:
+    """将中文港口名翻译为英文"""
+    port_map = {
+        '广州南沙': 'Guangzhou nansha Port',
+        '广州': 'Guangzhou Port',
+        '深圳': 'Shenzhen Port',
+        '上海': 'Shanghai Port',
+        '天津': 'Tianjin Port',
+        '青岛': 'Qingdao Port',
+        '宁波': 'Ningbo Port',
+        '厦门': 'Xiamen Port',
+    }
+    for cn, en in port_map.items():
+        if cn in port_cn:
+            return en
+    return port_cn
+
+
+def process_vehicles(vehicles: list, trade_term: str) -> list:
+    """
+    处理车型列表,匹配价格表或提示用户提供价格
+    
+    Returns:
+        (processed_vehicles, missing_prices)
+        processed_vehicles: 包含单价的车型列表
+        missing_prices: 未匹配到价格的车型列表(需用户补充)
+    """
+    processed = []
+    missing = []
+    
+    for v in vehicles:
+        model_code = v.get('model_code', '')
+        
+        # 如果用户已提供单价,直接使用
+        if v.get('unit_price_usd') is not None:
+            processed.append(v)
+            continue
+        
+        # 从价格表匹配
+        match_result = match_vehicle(model_code, trade_term)
+        if match_result and match_result.get('unit_price_usd'):
+            v['unit_price_usd'] = match_result['unit_price_usd']
+            v['config_desc'] = build_vehicle_description(v, match_result)
+            v['_match_result'] = match_result
+            processed.append(v)
+        else:
+            # 未匹配到价格
+            missing.append(v)
+    
+    return processed, missing
+
+
+def calculate_summary(vehicles: list) -> dict:
+    """计算汇总信息"""
+    total_qty = sum(v['quantity'] for v in vehicles)
+    total_amount = sum(v['unit_price_usd'] * v['quantity'] for v in vehicles)
+    
+    deposit_30, balance_70 = calculate_payment_split(total_amount)
+    
+    return {
+        'total_qty': total_qty,
+        'total_amount': round(total_amount, 2),
+        'total_amount_int': round(total_amount),
+        'deposit_30': deposit_30,
+        'balance_70': balance_70,
+        'amount_in_words': format_say_us_only(total_amount),
+    }
+
+
+def generate_proforma_invoice(order: dict, vehicles: list, summary: dict, 
+                               contract_no: str, output_dir: str) -> str:
+    """生成Proforma Invoice"""
+    import openpyxl
+    from openpyxl.utils import get_column_letter
+    
+    # 复制模板
+    template_path = get_asset_path('proforma-invoice-template.xlsx')
+    output_filename = f"{contract_no}-Proforma Invoice.xlsx"
+    output_path = os.path.join(output_dir, output_filename)
+    shutil.copy(template_path, output_path)
+    
+    # 打开并修改
+    wb = openpyxl.load_workbook(output_path)
+    ws = wb['INVOICE']
+    
+    # 买方信息
+    ws['B7'] = f"TO:  {order.get('buyer_en', '')}"
+    ws['G7'] = f"Contract NO.: {contract_no}"
+    ws['H8'] = format_date_for_cell()
+    ws['B8'] = f"ADD: {order.get('address_en', '')}"
+    ws['B9'] = f"Tel: {order.get('tel', '')}"
+    
+    # 港口信息
+    departure_port = translate_port(order.get('departure_port', 'Guangzhou nansha Port'))
+    trade_term = order.get('trade_term', 'FCA')
+    
+    # 商品明细 (B17-H22)
+    start_row = 17
+    for i, v in enumerate(vehicles):
+        row = start_row + i
+        if row > 22:
+            break  # 模板最多支持到22行
+        
+        desc = v.get('config_desc') or build_vehicle_description(v, v.get('_match_result'))
+        if not desc:
+            desc = f"Wuling {v['model_code']}, {v['engine_code']}"
+        
+        ws.cell(row=row, column=2, value=desc)  # B列: DESCRIPTION
+        ws.cell(row=row, column=3, value=v['quantity'])  # C列: QTY
+        ws.cell(row=row, column=4, value='UNIT')  # D列
+        ws.cell(row=row, column=5, value='USD')  # E列
+        ws.cell(row=row, column=6, value=v['unit_price_usd'])  # F列: U.PRICE
+        ws.cell(row=row, column=7, value='USD')  # G列
+        # H列保留公式 =C*F
+    
+    # 清除未使用的行(如果车辆少于模板默认行数)
+    for row in range(start_row + len(vehicles), 23):
+        for col in range(2, 9):
+            ws.cell(row=row, column=col, value=None)
+    
+    # 汇总信息
+    ws['E23'] = f"  {trade_term}   {departure_port} "
+    ws['B24'] = summary['amount_in_words']
+    
+    # Delivery terms
+    ws['B27'] = f"1. Delivery terms:  {trade_term} {departure_port}  "
+    
+    # Payment terms
+    payment_text = (
+        f"3. Payment terms:\n"
+        f"1. 30% of the Total Amount (USD {summary['deposit_30']:,}) shall be paid to Seller within 5 working days from order confirmation.\n"
+        f"2. 70% of the Total Amount (USD {summary['balance_70']:,}) shall be paid to Seller within 10 working days before delivery.     "
+    )
+    ws['B29'] = payment_text
+    
+    wb.save(output_path)
+    wb.close()
+    
+    return output_path
+
+
+def generate_sales_contract(order: dict, vehicles: list, summary: dict,
+                            contract_no: str, output_dir: str) -> str:
+    """生成销售合同 (Word)"""
+    from docx import Document
+    from docx.shared import Pt
+    
+    # 复制模板
+    template_path = get_asset_path('vehicle-sales-contract-template.docx')
+    output_filename = f"{contract_no}-车辆销售合同.docx"
+    output_path = os.path.join(output_dir, output_filename)
+    shutil.copy(template_path, output_path)
+    
+    # 打开并修改
+    doc = Document(output_path)
+    
+    departure_port = translate_port(order.get('departure_port', 'Guangzhou nansha Port'))
+    trade_term = order.get('trade_term', 'FCA')
+    
+    # 1. 替换段落文本
+    for para in doc.paragraphs:
+        text = para.text
+        if not text:
+            continue
+        
+        # 合同编号
+        if 'No.:' in text and not re.search(r'No\.:\s*\S', text):
+            para.text = text.replace('No.:', f'No.: {contract_no}')
+        
+        # 签署日期
+        if 'Signature Date:' in text and not re.search(r'Signature Date:\s*\d', text):
+            para.text = text.replace('Signature Date:', f'Signature Date: {format_date()}')
+        
+        # 买方英文
+        if text.strip() == 'Buyer:':
+            para.text = f"Buyer: {order.get('buyer_en', '')}"
+        
+        # 买方中文
+        if text.strip() == '买方:':
+            para.text = f"买方:{order.get('buyer_cn', '')}"
+        
+        # 英文地址 (注意模板中使用 \xa0 不间断空格)
+        if re.search(r'ADD\s*[::]', text) and len(text.strip()) < 10:
+            para.text = f"ADD: {order.get('address_en', '')}"
+        
+        # 中文地址
+        if re.search(r'地址\s*[::]', text) and len(text.strip()) < 10:
+            para.text = f"地址:{order.get('address_cn', '')}"
+        
+        # 电话
+        if text.strip() == 'Tel电话:':
+            para.text = f"Tel电话: {order.get('tel', '')}"
+        
+        # 出口目的地
+        if 'Export Destination:' in text:
+            para.text = f"Export Destination:  {order.get('destination_country', '')}"
+        
+        # 贸易条款
+        if 'Incoterms:' in text:
+            para.text = f"Incoterms: {trade_term} {departure_port}"
+        
+        # 付款条款
+        if '30% of the Total Amount' in text and 'shall be paid' in text:
+            para.text = (
+                f"30% of the Total Amount shall be paid to the Seller by T/T before the production. "
+                f"70% of the Total Amount shall be paid to the Seller by the Buyer by means of T/T before the delivery of vehicles."
+            )
+        
+        # 付款条款中文
+        if '生产前,买方向卖方通过电汇支付' in text:
+            para.text = (
+                f"生产前,买方向卖方通过电汇支付本合同项下30% 货款。"
+                f"发运前,买方向卖方通过电汇支付本合同项下70% 货款。"
+            )
+    
+    # 2. 替换表格内容
+    for table in doc.tables:
+        # 判断表格类型:车辆明细表 或 银行账户表
+        first_cell_text = table.rows[0].cells[0].text.strip() if table.rows else ''
+        
+        if 'DESCRIPTION' in first_cell_text or '货物描述' in first_cell_text:
+            # 车辆明细表 (Table0)
+            _fill_vehicle_table(table, vehicles, summary, trade_term, departure_port)
+        
+        # 银行账户表保持原样(Table1)
+        # 商标表保持原样(Table2)
+    
+    doc.save(output_path)
+    return output_path
+
+
+def _fill_vehicle_table(table, vehicles, summary, trade_term, departure_port):
+    """填充销售合同中的车辆明细表"""
+    # 表头行通常是第0行
+    # 数据行从第1行开始
+    # 汇总行需要找到包含 "Total" 或 "总数量" 的行
+    
+    data_start_row = 1
+    
+    # 填充车辆数据
+    for i, v in enumerate(vehicles):
+        row_idx = data_start_row + i
+        if row_idx >= len(table.rows):
+            break
+        
+        row = table.rows[row_idx]
+        desc = v.get('config_desc') or build_vehicle_description(v, v.get('_match_result'))
+        if not desc:
+            desc = f"Wuling {v['model_code']}, {v['engine_code']}"
+        
+        # 描述单元格 (通常是第0列)
+        if len(row.cells) > 0:
+            row.cells[0].text = f"Model: {v['model_code']}, {v['engine_code']}\n\nColor: {v.get('color', '1')}"
+        
+        # 数量 (第1列)
+        if len(row.cells) > 1:
+            row.cells[1].text = str(v['quantity'])
+        
+        # 单价 (第2列)
+        if len(row.cells) > 2:
+            row.cells[2].text = str(v['unit_price_usd'])
+        
+        # 金额 (第3列)
+        if len(row.cells) > 3:
+            row.cells[3].text = str(v['unit_price_usd'] * v['quantity'])
+    
+    # 清空多余的数据行(如果车辆数少于模板默认行数)
+    for row_idx in range(data_start_row + len(vehicles), len(table.rows)):
+        row = table.rows[row_idx]
+        # 检查是否是汇总行
+        if any('Total' in cell.text or '总数量' in cell.text or 'SAY USD' in cell.text for cell in row.cells):
+            # 填充汇总行
+            total_text = (
+                f"Total: {summary['total_qty']} units  总数量: {summary['total_qty']} 辆    \n"
+                f"Total Amount:  USD {summary['total_amount']:.2f}  总金额: 美元 {summary['total_amount']:.2f}\n"
+                f" (Say USD: {summary['amount_in_words'].replace('SAY US ', '').replace(' only', '')}  ONLY)\n"
+                f"(合计美元:美元整)\n"
+                f"{trade_term}  {departure_port}    {trade_term}  {departure_port}"
+            )
+            for cell in row.cells:
+                cell.text = total_text
+            break
+        else:
+            # 清空非汇总行
+            for cell in row.cells:
+                cell.text = ''
+
+
+def generate_contracts(order_text: str, output_dir: str = '.', 
+                        user_prices: dict = None) -> dict:
+    """
+    主函数:根据订单文本生成合同
+    
+    Args:
+        order_text: 订单信息文本
+        output_dir: 输出目录
+        user_prices: 用户手动提供的价格 {model_code: price}
+    
+    Returns:
+        {
+            'pi_path': str,
+            'contract_path': str,
+            'contract_no': str,
+            'summary': dict,
+            'missing_prices': list,
+        }
+    """
+    # 1. 解析订单
+    order = parse_order_info(order_text)
+    
+    # 2. 生成合同编号
+    contract_no = generate_contract_no()
+    
+    # 3. 处理车型价格
+    vehicles = order.get('vehicles', [])
+    trade_term = order.get('trade_term', 'FCA')
+    
+    # 应用用户提供的价格
+    if user_prices:
+        for v in vehicles:
+            if v['model_code'] in user_prices:
+                v['unit_price_usd'] = user_prices[v['model_code']]
+    
+    processed, missing = process_vehicles(vehicles, trade_term)
+    
+    # 如果有缺失价格,返回提示信息
+    if missing:
+        return {
+            'success': False,
+            'missing_prices': missing,
+            'message': f"以下车型在价格表中未找到,请提供单价USD: {[v['model_code'] for v in missing]}",
+        }
+    
+    # 4. 计算汇总
+    summary = calculate_summary(processed)
+    
+    # 5. 生成文件
+    os.makedirs(output_dir, exist_ok=True)
+    
+    pi_path = generate_proforma_invoice(order, processed, summary, contract_no, output_dir)
+    contract_path = generate_sales_contract(order, processed, summary, contract_no, output_dir)
+    
+    return {
+        'success': True,
+        'contract_no': contract_no,
+        'pi_path': pi_path,
+        'contract_path': contract_path,
+        'summary': summary,
+        'missing_prices': [],
+    }
+
+
+if __name__ == '__main__':
+    import argparse
+    
+    parser = argparse.ArgumentParser(description='Generate export contracts from order info')
+    parser.add_argument('order_file', help='Path to order info text file')
+    parser.add_argument('-o', '--output', default='.', help='Output directory')
+    parser.add_argument('-p', '--prices', help='JSON string of manual prices {"LZW1028SPY": 6176}')
+    
+    args = parser.parse_args()
+    
+    with open(args.order_file, 'r', encoding='utf-8') as f:
+        order_text = f.read()
+    
+    user_prices = None
+    if args.prices:
+        import json
+        user_prices = json.loads(args.prices)
+    
+    result = generate_contracts(order_text, args.output, user_prices)
+    
+    import json as json_mod
+    sys.stdout.reconfigure(encoding='utf-8')
+    print(json_mod.dumps(result, ensure_ascii=False, indent=2))

+ 160 - 0
auto-generate-export-contracts/scripts/match_vehicle.py

@@ -0,0 +1,160 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+车型匹配脚本:在价格配置表中按ModelCode匹配车型信息
+"""
+
+import os
+import openpyxl
+
+# 价格列映射
+PRICE_COLUMN_MAP = {
+    'FOB': 'N',
+    'FCA': 'O',
+    'EXW': 'P',
+}
+
+PRICE_COL_INDEX = {
+    'FOB': 13,  # N列 (0-based index)
+    'FCA': 14,  # O列
+    'EXW': 15,  # P列
+}
+
+
+def get_price_config_path():
+    """获取价格配置表路径"""
+    script_dir = os.path.dirname(os.path.abspath(__file__))
+    return os.path.join(script_dir, '..', 'assets', 'vehicle-price-config.xlsx')
+
+
+def match_vehicle(model_code: str, trade_term: str = 'FCA'):
+    """
+    在价格配置表中匹配车型
+    
+    Args:
+        model_code: 车型型号,如 LZW5030XXYLGHUG
+        trade_term: 贸易条款,如 FCA/FOB/EXW
+    
+    Returns:
+        dict or None: {
+            'unit_price_usd': float,
+            'description_cn': str,
+            'description_en': str,
+            'model_code': str,
+            'engine_code': str,
+            'version': str,
+            'config_desc': str,
+            'sheet_name': str,
+        }
+    """
+    if not model_code:
+        return None
+    
+    model_code = model_code.strip().upper()
+    trade_term = trade_term.upper()
+    
+    # 默认使用FCA价格列
+    price_col_idx = PRICE_COL_INDEX.get(trade_term, 14)
+    
+    config_path = get_price_config_path()
+    if not os.path.exists(config_path):
+        print(f"[WARN] Price config not found: {config_path}")
+        return None
+    
+    wb = openpyxl.load_workbook(config_path, data_only=True)
+    
+    for sheet_name in wb.sheetnames:
+        ws = wb[sheet_name]
+        for row in ws.iter_rows(min_row=2, max_row=ws.max_row, values_only=False):
+            # G列是型号 (index 6)
+            cell_g = row[6] if len(row) > 6 else None
+            if cell_g and cell_g.value:
+                # ModelCode可能包含换行符,如 "LZW5030XXYLGHUG\n国六B(RDE)"
+                cell_val = str(cell_g.value).strip().upper()
+                # 分割换行符取第一行作为ModelCode
+                cell_model = cell_val.split('\n')[0].strip()
+                if cell_model == model_code:
+                    # 找到匹配
+                    result = {
+                        'model_code': model_code,
+                        'sheet_name': sheet_name,
+                    }
+                    
+                    # B列: 市场名称
+                    result['description_cn'] = str(row[1].value).strip() if row[1].value else ''
+                    # C列: 版本
+                    result['version'] = str(row[2].value).strip() if row[2].value else ''
+                    # F列: 系别 (中英文)
+                    series_val = str(row[5].value).strip() if row[5].value else ''
+                    result['description_en'] = series_val
+                    # I列: 发动机代码
+                    result['engine_code'] = str(row[8].value).strip() if row[8].value else ''
+                    # J列: 主要配置描述
+                    result['config_desc'] = str(row[9].value).strip() if row[9].value else ''
+                    
+                    # 价格列
+                    price_cell = row[price_col_idx] if len(row) > price_col_idx else None
+                    if price_cell and price_cell.value:
+                        try:
+                            result['unit_price_usd'] = round(float(price_cell.value), 2)
+                        except (ValueError, TypeError):
+                            result['unit_price_usd'] = None
+                    else:
+                        result['unit_price_usd'] = None
+                    
+                    wb.close()
+                    return result
+    
+    wb.close()
+    return None
+
+
+def build_vehicle_description(vehicle: dict, match_result: dict = None) -> str:
+    """
+    构建PI中的商品描述文本
+    
+    格式示例: Wuling LZW5030XXYLGHUG, AGMC,1.999L
+    """
+    import re
+    
+    model_code = vehicle.get('model_code', '')
+    engine_code = vehicle.get('engine_code', '')
+    
+    # 尝试从匹配结果获取排量信息
+    displacement = ''
+    
+    # 1. 从配置描述中提取排量,如 "(1.999L)"
+    if match_result and match_result.get('config_desc'):
+        m = re.search(r'\(([\d.]+L)\)', match_result['config_desc'])
+        if m:
+            displacement = m.group(1)
+    
+    # 2. 从发动机代码中提取排量,如 "VR5\n(1.999L)\n5MT"
+    if not displacement and match_result and match_result.get('engine_code'):
+        m = re.search(r'\(([\d.]+L)\)', match_result['engine_code'])
+        if m:
+            displacement = m.group(1)
+    
+    # 3. 从版本信息推断
+    if not displacement and vehicle.get('version'):
+        m = re.search(r'([\d.]+L)', vehicle['version'])
+        if m:
+            displacement = m.group(1)
+    
+    # 4. 从车型名称中推断(如 "2.0L 5MT")
+    if not displacement and vehicle.get('name_cn'):
+        m = re.search(r'([\d.]+L)', vehicle['name_cn'])
+        if m:
+            displacement = m.group(1)
+    
+    parts = [p for p in [f'Wuling {model_code}', engine_code, displacement] if p]
+    return ', '.join(parts)
+
+
+if __name__ == '__main__':
+    # 测试
+    result = match_vehicle('LZW5030XXYLGHUG', 'FCA')
+    print(result)
+    
+    result2 = match_vehicle('LZW1028SPY', 'FCA')
+    print(result2)

+ 66 - 0
auto-generate-export-contracts/scripts/number_to_words.py

@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+金额英文大写转换
+"""
+
+from num2words import num2words
+import math
+
+
+def amount_to_english_words(amount: float) -> str:
+    """
+    将金额转换为英文大写
+    
+    Args:
+        amount: 金额数字,如 14488
+    
+    Returns:
+        英文大写字符串,如 "FOURTEEN THOUSAND FOUR HUNDRED AND EIGHTY-EIGHT"
+    """
+    if amount is None or amount < 0:
+        return ''
+    
+    # 四舍五入到整数
+    amount_int = round(amount)
+    
+    # 转换为英文单词并大写
+    words = num2words(amount_int, lang='en')
+    return words.upper().replace('-', ' ').replace(',', '')
+
+
+def calculate_payment_split(total_amount: float) -> tuple:
+    """
+    计算30%和70%的付款金额
+    
+    规则:30%向上取整,70% = 总金额 - 30%
+    确保 30% + 70% = 总金额
+    
+    Returns:
+        (deposit_30, balance_70)
+    """
+    total_int = round(total_amount)
+    deposit_30 = math.ceil(total_int * 0.30)
+    balance_70 = total_int - deposit_30
+    return deposit_30, balance_70
+
+
+def format_say_us_only(amount: float) -> str:
+    """
+    格式化为 "SAY US {大写} only"
+    
+    Args:
+        amount: 金额数字
+    
+    Returns:
+        如 "SAY US FOURTEEN THOUSAND FOUR HUNDRED AND EIGHTY EIGHT only"
+    """
+    words = amount_to_english_words(amount)
+    return f"SAY US {words} only"
+
+
+if __name__ == '__main__':
+    # 测试
+    print(format_say_us_only(14488))
+    print(format_say_us_only(4346.4))
+    print(format_say_us_only(10141.6))

+ 258 - 0
auto-generate-export-contracts/scripts/parse_order_info.py

@@ -0,0 +1,258 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+订单信息解析脚本:支持编号列表格式和自然语言格式
+"""
+
+import re
+
+
+def parse_structured_order(text: str) -> dict:
+    """
+    解析编号列表格式的订单信息
+    """
+    result = {}
+    
+    # 基本信息提取
+    m = re.search(r'姓名[::]\s*(.+?)[;;\n]', text)
+    result['sales_name'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'任职[::]\s*(.+?)[;;\n]', text)
+    result['sales_title'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'合同签约公司中文名称[::]\s*(.+?)[;;\n]', text)
+    result['buyer_cn'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'合同签约公司英文名称[::]\s*(.+?)(?:\n|$)', text)
+    result['buyer_en'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'合同签约公司中文地址[::]\s*(.+?)[;;\n]', text)
+    result['address_cn'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'合同签约公司英文地址[::]\s*(.+?)(?:\n|$)', text)
+    result['address_en'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'合同联系电话[::]\s*([\d+\-]+)', text)
+    result['tel'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'国内出发港口[::]\s*(.+?)[;;\n]', text)
+    result['departure_port'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'目的地国家[::]\s*(.+?)(?:\n|$)', text)
+    result['destination_country'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'目的地港口[::]\s*(.+?)(?:\n|$)', text)
+    result['destination_port'] = m.group(1).strip() if m else ''
+    
+    m = re.search(r'结算方式[::]\s*(FCA|FOB|EXW|CIF)', text, re.IGNORECASE)
+    result['trade_term'] = m.group(1).upper() if m else 'FCA'
+    
+    # 车型列表解析 - 使用行扫描方式
+    vehicles = []
+    lines = text.split('\n')
+    
+    i = 0
+    while i < len(lines):
+        line = lines[i].strip()
+        # 匹配车型起始行: (数字)... 型号:XXX
+        m = re.match(r'[((](\d+)[))]\s*(.+?)\s*型号[::]\s*(\w+)', line)
+        if m:
+            seq = m.group(1)
+            name_part = m.group(2).strip()
+            model_code = m.group(3).strip()
+            
+            # 解析车型名称(中英文分离)
+            name_cn, name_en = split_name_cn_en(name_part)
+            
+            # 下一行提取排放、发动机代码、数量、颜色
+            engine_code = ''
+            emission = ''
+            quantity = 1
+            color = ''
+            
+            if i + 1 < len(lines):
+                next_line = lines[i + 1].strip()
+                # 模式: 排放标准  代码:XXX  X台颜色
+                m2 = re.search(r'(.+?)\s+代码[::]\s*(\w+)\s+(\d+)台(\w+)', next_line)
+                if m2:
+                    emission = m2.group(1).strip()
+                    engine_code = m2.group(2).strip()
+                    quantity = int(m2.group(3))
+                    color = m2.group(4).strip()
+                    i += 1  # 跳过已处理的下一行
+            
+            vehicles.append({
+                'seq': seq,
+                'name_cn': name_cn,
+                'name_en': name_en,
+                'model_code': model_code,
+                'emission': emission,
+                'engine_code': engine_code,
+                'quantity': quantity,
+                'color': color,
+                'unit_price_usd': None,
+                'config_desc': '',
+            })
+        i += 1
+    
+    result['vehicles'] = vehicles
+    return result
+
+
+def split_name_cn_en(name_part: str) -> tuple:
+    """
+    将车型描述分割为中文名和英文名
+    
+    例如:
+    '荣光新双排货车  N350 Double Cab Pickup,-' -> ('荣光新双排货车', 'N350 Double Cab Pickup')
+    '五菱荣光新卡双后轮 2.0L 5MT 单排' -> ('五菱荣光新卡双后轮 2.0L 5MT 单排', '')
+    """
+    # 移除末尾的标点符号
+    name_part = re.sub(r'[,,、\-]+$', '', name_part).strip()
+    
+    # 查找中英文分界点(连续英文字母序列)
+    # 策略:找到最后一个中文字符之后的主要英文部分
+    # 更简单的策略:如果包含明显的中英分界(中文字符后接大段英文)
+    
+    # 匹配模式: 中文部分 + 空格 + 英文部分(英文部分以字母开头,包含字母和空格)
+    m = re.match(r'([\u4e00-\u9fff][\u4e00-\u9fff\s\d.]+?)\s+([A-Za-z][A-Za-z\s\(\)]+)$', name_part)
+    if m:
+        return m.group(1).strip(), m.group(2).strip()
+    
+    # 如果没有明显的英文部分,全部作为中文名
+    if re.search(r'[\u4e00-\u9fff]', name_part):
+        return name_part, ''
+    
+    # 如果没有中文,全部作为英文名
+    return '', name_part
+
+
+def parse_natural_language(text: str) -> dict:
+    """
+    使用正则从自然语言中提取关键字段
+    """
+    result = {}
+    
+    # 买方公司名称
+    m = re.search(r'(?:买方|买方公司|签约公司|客户)[是|::]\s*([^,,;;。\n]+)', text)
+    if m:
+        name = m.group(1).strip()
+        if re.search(r'[\u4e00-\u9fff]', name):
+            result['buyer_cn'] = name
+            result['buyer_en'] = ''
+        else:
+            result['buyer_en'] = name
+            result['buyer_cn'] = ''
+    
+    if not result.get('buyer_en'):
+        m = re.search(r'英文名称[是|::]\s*([^,,;;。\n]+)', text)
+        if m:
+            result['buyer_en'] = m.group(1).strip()
+    
+    if not result.get('buyer_cn'):
+        m = re.search(r'中文名称[是|::]\s*([^,,;;。\n]+)', text)
+        if m:
+            result['buyer_cn'] = m.group(1).strip()
+    
+    # 地址
+    m = re.search(r'(?:地址|ADD)[是|::]\s*([^,,;;。\n]+)', text)
+    if m:
+        addr = m.group(1).strip()
+        if re.search(r'[\u4e00-\u9fff]', addr):
+            result['address_cn'] = addr
+            result['address_en'] = ''
+        else:
+            result['address_en'] = addr
+            result['address_cn'] = ''
+    
+    # 电话
+    m = re.search(r'(?:电话|Tel|联系电话)[是|::]\s*([\d+\-]+)', text)
+    if m:
+        result['tel'] = m.group(1).strip()
+    
+    # 贸易条款
+    m = re.search(r'(?:贸易条款|结算方式|Incoterm)[是|::]?\s*(FCA|FOB|EXW|CIF)', text, re.IGNORECASE)
+    result['trade_term'] = m.group(1).upper() if m else 'FCA'
+    
+    # 出发港口
+    m = re.search(r'(?:出发港|出发港口|起运港)[是|::]\s*([^,,;;。\n]+)', text)
+    result['departure_port'] = m.group(1).strip() if m else ''
+    
+    # 目的地国家
+    m = re.search(r'(?:目的地国家|目的国|出口到|发往)[是|::]?\s*([^,,;;。\n]+)', text)
+    result['destination_country'] = m.group(1).strip() if m else ''
+    
+    # 车型解析
+    vehicles = []
+    
+    # 模式1: ... 型号:XXX ... 代码:XXX ... X台
+    pattern = r'(?:车型[\d]*[::、..]?\s*)?(.+?)\s+(?:型号[::]\s*)?(LZW\w+).*?(?:代码[::]\s*)?(\w+).*?(\d+)台'
+    matches = re.findall(pattern, text, re.DOTALL)
+    for m in matches:
+        name_part = m[0].strip()
+        name_cn, name_en = split_name_cn_en(name_part)
+        vehicles.append({
+            'name_cn': name_cn,
+            'name_en': name_en,
+            'model_code': m[1].strip(),
+            'emission': '',
+            'engine_code': m[2].strip(),
+            'quantity': int(m[3]),
+            'color': '',
+            'unit_price_usd': None,
+            'config_desc': '',
+        })
+    
+    if not vehicles:
+        pattern2 = r'(LZW\w+)\s+(?:发动机代码[::]\s*)?(\w+).*?(\d+)台'
+        matches = re.findall(pattern2, text)
+        for m in matches:
+            vehicles.append({
+                'name_cn': '',
+                'name_en': '',
+                'model_code': m[0].strip(),
+                'emission': '',
+                'engine_code': m[1].strip(),
+                'quantity': int(m[2]),
+                'color': '',
+                'unit_price_usd': None,
+                'config_desc': '',
+            })
+    
+    result['vehicles'] = vehicles
+    return result
+
+
+def parse_order_info(text: str) -> dict:
+    """
+    主解析函数:自动判断格式并解析
+    """
+    text = text.strip()
+    
+    # 判断是否为结构化编号列表格式
+    is_structured = bool(re.search(r'^\d+[..]\s*(姓名|任职|意向车型|合同签约公司)', text, re.MULTILINE))
+    
+    if is_structured:
+        result = parse_structured_order(text)
+    else:
+        result = parse_natural_language(text)
+    
+    # 填充默认值
+    if not result.get('trade_term'):
+        result['trade_term'] = 'FCA'
+    if not result.get('departure_port'):
+        result['departure_port'] = 'Guangzhou nansha Port'
+    
+    return result
+
+
+if __name__ == '__main__':
+    import json
+    import sys
+    sys.stdout.reconfigure(encoding='utf-8')
+    
+    with open('../assets/订单合同信息案例.txt', encoding='utf-8') as f:
+        test_text = f.read()
+    
+    result = parse_order_info(test_text)
+    print(json.dumps(result, ensure_ascii=False, indent=2))

BIN
auto-generate-export-contracts/test_output/HLXYWPA20260530-Proforma Invoice.xlsx


BIN
auto-generate-export-contracts/test_output/HLXYWPA20260530-车辆销售合同.docx


BIN
auto-generate-export-contracts/test_output/~$HLXYWPA20260530-Proforma Invoice.xlsx


BIN
auto-generate-export-contracts/test_output/~$XYWPA20260530-车辆销售合同.docx


+ 291 - 0
skill-development-plan.md

@@ -0,0 +1,291 @@
+# 海外合同自动生成 Skill - 开发文档(定稿版)
+
+## 1. Skill 概述
+
+### 1.1 名称
+`auto-generate-export-contracts`
+
+### 1.2 功能描述
+根据用户提供的订单合同信息,自动基于模板生成:
+1. **车辆销售合同 (Vehicle Sales Contract)** - Word (.docx) 格式
+2. **Proforma Invoice** - Excel (.xlsx) 格式
+
+如果订单信息中没有车型价格,则从内置的`单一车型价格表+配置.xlsx`中按型号匹配查找。若价格表中无匹配,则提示用户提供单价。
+
+### 1.3 触发条件
+当用户:
+- 提供订单合同信息,要求生成销售合同和/或Proforma Invoice
+- 提到"生成合同"、"制作PI"、"Proforma Invoice"、"销售合同"等相关关键词
+- 提供包含车型、买方信息、贸易条款等订单信息时
+
+---
+
+## 2. 输入格式定义
+
+### 2.1 支持两种输入格式
+
+**格式A:结构化编号列表(标准格式)**
+```
+1. 姓名:陈凯武;
+2.任职:经理;
+3.意向车型:                 
+(1) 荣光新双排货车  N350 Double Cab Pickup,- 型号:LZW1028SPY
+国V China Ⅴ  代码:G33N    1台银色
+(2)五菱荣光新卡双后轮 2.0L 5MT 单排 型号:LZW5030XXYLGHUG
+国六B(RDE)  代码:AGMC  1台银色
+4.合同签约公司中文名称:财货通(香港)国际供应链有限公司;
+5.合同签约公司英文名称:Finmo (HongKong)International supply chain Co.,Ltd.
+6.合同签约公司中文地址:香港德輔道西 93-97 號聯威商業大廈 13 樓 B-C 室
+7.合同签约公司英文地址:FLAT B&C,13/F.,LUEN WAI COMMERCIAL BUILDING,93-97 DES VOEUX ROAD WEST,HONG KONG
+8.合同联系电话:13640613242
+9.国内出发港口:广州南沙;
+10.目的地国家:巴拿马Panama
+11.目的地港口:巴尔博亚港 Balboa
+12.结算方式:FCA
+```
+
+**格式B:自然语言描述**
+```
+帮我生成销售合同,买方是财货通(香港)国际供应链有限公司,英文名Finmo (HongKong)International supply chain Co.,Ltd.,要买两辆五菱车:
+1. 荣光新双排货车 LZW1028SPY,发动机代码G33N,1台银色
+2. 五菱荣光新卡双后轮 LZW5030XXYLGHUG,发动机代码AGMC,1台银色
+贸易条款FCA,出发港广州南沙,目的地巴拿马,联系电话13640613242
+```
+
+### 2.2 需要提取的核心字段
+
+| 字段 | 说明 | 是否必填 |
+|---|---|---|
+| 买方公司中文名称 | | 必填 |
+| 买方公司英文名称 | | 必填 |
+| 买方中文地址 | | 必填 |
+| 买方英文地址 | | 必填 |
+| 合同联系电话 | | 必填 |
+| 车型列表 | 每辆含:型号、发动机代码、数量、颜色 | 必填(至少1辆) |
+| 贸易条款 | FCA / FOB / EXW / CIF 等 | 必填 |
+| 出发港口 | | 必填 |
+| 目的地国家 | | 必填 |
+| 目的地港口 | | 可选 |
+| 销售人员姓名 | | 可选 |
+| 销售人员职位 | | 可选 |
+
+**注意**:订单中的"付款公司"信息(创兴银行/账户等)**不在合同中体现**,模板中保留固定的卖方收款账户。
+
+### 2.3 Skill自动生成的字段
+
+| 字段 | 生成规则 |
+|---|---|
+| 合同编号 | `HLXYWPA{年月日}`,如 `HLXYWPA20260530` |
+| 签署日期 | 当前日期 |
+| 卖方信息 | 固定模板值 |
+| 收款银行信息 | 固定模板值(卖方账户) |
+
+---
+
+## 3. 车型价格配置表
+
+### 3.1 数据来源
+`assets/vehicle-price-config.xlsx`(由 `单一车型价格表+配置.xlsx` 转换而来)
+
+### 3.2 Sheet列表
+| 索引 | Sheet名 |
+|---|---|
+| 0 | 五菱之光小卡 |
+| 1 | 五菱之光 |
+| 2 | 五菱宏光v |
+| 3 | 新宏光S客车 |
+| 4 | 星光560 |
+| 5 | 星光730 |
+| 6 | 缤果 |
+| 7 | 星光 |
+| 8 | 星光s |
+| 9 | 悦也plus |
+| 10 | 云海 |
+| 11 | 宏光s3 |
+| 12 | 星驰 |
+| 13 | 扬光 |
+| 14 | 迷你 |
+| 15 | 五菱荣光新卡燃油 |
+| 16 | 荣光小卡 |
+
+### 3.3 列定义
+| 列 | 字段名 | 说明 |
+|---|---|---|
+| A | 车型车系 | Vehicle Series |
+| B | 市场名称 | Market Name (中文) |
+| C | 版本 | Version |
+| E | 官方指导价 | MSRP (RMB) |
+| F | 系别 | Series Code (中英文) |
+| G | 型号 | Model (ModelCode) |
+| H | 车型代码 | Model Code |
+| I | 发动机代码 | Engine Code |
+| J | 主要配置描述 | Major Configurations Description (中英文) |
+| K | FOB价格(人民币) | FOB Price (RMB) |
+| L | FCA价格(人民币) | FCA Price (RMB) |
+| M | EXW价格(人民币) | EXW Price (RMB) |
+| N | FOB价格(美元) | FOB Price (USD) |
+| O | FCA价格(美元) | FCA Price (USD) |
+| P | EXW价格(美元) | EXW Price (USD) |
+
+### 3.4 匹配逻辑
+```python
+def match_vehicle(model_code: str, trade_term: str) -> dict:
+    """
+    遍历所有sheet,在G列(型号/ModelCode)精确匹配model_code
+    根据trade_term选择对应USD价格列:
+      - FCA -> O列
+      - FOB -> N列  
+      - EXW -> P列
+    返回: {
+        'unit_price_usd': float,
+        'description_cn': str,   # B列市场名称
+        'description_en': str,   # F列系别英文
+        'model_code': str,       # G列
+        'engine_code': str,      # I列
+        'version': str,          # C列
+    }
+    """
+```
+
+**匹配失败处理**:如果G列精确匹配不到输入的ModelCode,**提示用户手动提供该车型的单价USD和配置描述**。
+
+---
+
+## 4. 模板替换规则
+
+### 4.1 Proforma Invoice 模板替换字段
+
+| 单元格 | 替换内容 |
+|---|---|
+| B7 | TO: {买方公司英文名称} |
+| G7 | Contract NO.: {合同编号} |
+| H8 | DATE: {当前日期} |
+| B8 | ADD: {买方公司英文地址} |
+| B9 | Tel: {合同联系电话} |
+| B12 | ORIGINAL: LIUZHOU CHINA(固定) |
+| G12 | SHIPPING MARKS: N/M(固定) |
+| B17-B22 | 每行一辆车: 配置描述文本 |
+| C17-C22 | 数量 |
+| F17-F22 | 单价USD |
+| H17-H22 | 金额公式 `=C*F` |
+| E23 | FCA {出发港口} |
+| H23 | =SUM(H17:H22) 保留公式 |
+| B24 | SAY US {英文大写金额} only |
+| B27 | Delivery terms: FCA {出发港口} |
+| B29 | Payment terms(30%和70%动态计算) |
+
+**银行账户信息区**:保持模板固定值(卖方GUANGXI HAOLING收款账户)。
+
+### 4.2 销售合同模板替换字段
+
+| 位置 | 替换内容 |
+|---|---|
+| No.: 后 | {合同编号} |
+| Signature Date: 后 | {当前日期} |
+| Buyer: | {买方公司英文名称} |
+| 买方: | {买方公司中文名称} |
+| ADD: | {买方公司英文地址} |
+| 地址: | {买方公司中文地址} |
+| Tel: | {合同联系电话} |
+| Export Destination: | {目的地国家} |
+| Incoterms: | {贸易条款} {出发港口} |
+
+**Table0(车辆明细表)**:填入车型、数量、单价、总金额。
+**Table1(银行账户)**:保持模板固定值。
+**付款条款**:30%和70%金额动态计算。
+
+---
+
+## 5. 核心工作流
+
+```
+Step 1: 接收用户输入(支持编号列表或自然语言)
+  └─> 使用LLM或正则解析提取核心字段
+
+Step 2: 车型信息处理
+  └─> IF 用户提供单价: 直接使用
+  └─> ELSE: 在价格表中按ModelCode精确匹配
+      └─> IF 匹配成功: 获取对应贸易条款的USD价格
+      └─> IF 匹配失败: 提示用户手动提供单价USD
+
+Step 3: 计算汇总
+  └─> 总数量、总金额、30%金额、70%金额、英文大写
+
+Step 4: 生成 Proforma Invoice (xlsx)
+  └─> 复制模板,替换字段,保留公式
+
+Step 5: 生成 销售合同 (docx)
+  └─> 复制模板,替换段落和表格内容
+
+Step 6: 输出
+  └─> 输出两个文件 + 固定提示语
+```
+
+---
+
+## 6. Skill 文件结构
+
+```
+auto-generate-export-contracts/
+├── SKILL.md
+├── scripts/
+│   ├── generate_contracts.py
+│   ├── match_vehicle.py
+│   ├── number_to_words.py
+│   └── parse_order_info.py
+├── assets/
+│   ├── proforma-invoice-template.xlsx
+│   ├── vehicle-sales-contract-template.docx
+│   └── vehicle-price-config.xlsx
+└── references/
+    └── field-mapping.md
+```
+
+---
+
+## 7. 关键技术方案
+
+### 7.1 依赖库
+- `python-docx`: 读写 Word 文档
+- `openpyxl`: 读写 Excel 文档
+- `num2words`: 数字转英文单词
+
+### 7.2 合同编号生成
+```python
+from datetime import datetime
+contract_no = f"HLXYWPA{datetime.now().strftime('%Y%m%d')}"
+# 示例: HLXYWPA20260530
+```
+
+### 7.3 数字转英文大写
+```python
+from num2words import num2words
+amount = 14488
+words = num2words(amount, lang='en').upper()
+# FOURTEEN THOUSAND FOUR HUNDRED AND EIGHTY-EIGHT
+```
+
+---
+
+## 8. 边界情况处理
+
+| 场景 | 处理方式 |
+|---|---|
+| 价格表无匹配 | 提示用户手动提供单价USD |
+| 用户提供的价格优先于价格表 | 使用用户值 |
+| 多款车型(>2辆) | PI支持最多6行,销售合同表格动态扩展 |
+| 非FCA/FOB/EXW贸易条款 | 替换文本,价格默认使用FCA列 |
+| 出发港口为空 | 默认"Guangzhou nansha Port" |
+| 目的地国家为空 | 提示用户补充 |
+| 用户只要求生成一个文件 | 支持单独生成 |
+
+---
+
+## 9. 输出规范
+
+### 9.1 文件名
+- Proforma Invoice: `{合同编号}-Proforma Invoice.xlsx`
+- 销售合同: `{合同编号}-车辆销售合同.docx`
+
+### 9.2 输出后固定提示语
+> 销售合同、Proforma Invoice已生成,报价员核对无误后再发送给客户。