diff --git a/eu_einvoice/european_e_invoice/doctype/e_invoice_import/e_invoice_import.js b/eu_einvoice/european_e_invoice/doctype/e_invoice_import/e_invoice_import.js index ec334fa..ffc6420 100644 --- a/eu_einvoice/european_e_invoice/doctype/e_invoice_import/e_invoice_import.js +++ b/eu_einvoice/european_e_invoice/doctype/e_invoice_import/e_invoice_import.js @@ -37,6 +37,17 @@ frappe.ui.form.on("E Invoice Import", { }; }); + frm.set_query("po_detail", "items", function (doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + query: "eu_einvoice.european_e_invoice.doctype.e_invoice_import.e_invoice_import.po_item_query", + filters: { + parent: doc.purchase_order, + item_code: row.item, + }, + }; + }); + frm.set_query("tax_account", "taxes", function (doc, cdt, cdn) { return { filters: { @@ -109,4 +120,25 @@ frappe.ui.form.on("E Invoice Item", { source_name: cdn, }); }, + + po_detail: function (frm, cdt, cdn) { + const row = locals[cdt][cdn]; + if (!row.po_detail || (row.item && row.uom) || !frappe.model.can_read("Purchase Order")) { + return; + } + + frappe + .xcall( + "eu_einvoice.european_e_invoice.doctype.e_invoice_import.e_invoice_import.get_po_item_details", + { po_detail: row.po_detail } + ) + .then((r) => { + if (r.item_code && !row.item) { + frappe.model.set_value(cdt, cdn, "item", r.item_code); + } + if (r.uom && !row.uom) { + frappe.model.set_value(cdt, cdn, "uom", r.uom); + } + }); + }, }); diff --git a/eu_einvoice/european_e_invoice/doctype/e_invoice_import/e_invoice_import.py b/eu_einvoice/european_e_invoice/doctype/e_invoice_import/e_invoice_import.py index 0cdb3fc..1af6b83 100644 --- a/eu_einvoice/european_e_invoice/doctype/e_invoice_import/e_invoice_import.py +++ b/eu_einvoice/european_e_invoice/doctype/e_invoice_import/e_invoice_import.py @@ -93,6 +93,8 @@ def before_save(self): self.guess_uom() self.guess_item_code() + self.guess_po_details() + def before_submit(self): if not self.supplier: frappe.throw(_("Please create or select a supplier before submitting")) @@ -142,7 +144,7 @@ def read_values_from_einvoice(self) -> None: self.parse_seller(doc.trade.agreement.seller) self.parse_buyer(doc.trade.agreement.buyer) - buyer_reference = str(doc.trade.agreement.buyer_order.issuer_assigned_id) + buyer_reference = doc.trade.agreement.buyer_order.issuer_assigned_id._text if ( not self.purchase_order and buyer_reference @@ -311,6 +313,35 @@ def guess_item_code(self): "parent", ) + def guess_po_details(self): + if not self.purchase_order: + for pi_row in self.items: + pi_row.po_detail = None + return + + purchase_order = frappe.get_doc("Purchase Order", self.purchase_order) + po_items = [ + frappe._dict( + name=po_row.name, + item_code=po_row.item_code, + unbilled_amount=po_row.amount - po_row.billed_amt, + ) + for po_row in purchase_order.items + ] + for pi_row in self.items: + if pi_row.po_detail and frappe.db.exists( + "Purchase Order Item", {"name": pi_row.po_detail, "parent": self.purchase_order} + ): + continue + + for po_row in po_items: + if po_row.item_code == pi_row.item and po_row.unbilled_amount >= pi_row.total_amount: + pi_row.po_detail = po_row.name + po_row.unbilled_amount -= pi_row.total_amount + break + else: + pi_row.po_detail = None + def add_seller_product_ids_to_items(self): for row in self.items: try: @@ -348,6 +379,10 @@ def create_purchase_invoice(source_name, target_doc=None): def post_process(source, target: "PurchaseInvoice"): target.set_missing_values() + def process_item_row(source, target, source_parent) -> None: + if source_parent.purchase_order: + target.purchase_order = source_parent.purchase_order + def process_tax_row(source, target, source_parent) -> None: target.charge_type = "Actual" @@ -382,7 +417,9 @@ def process_payment_term(source, target, source_parent): "billed_quantity": "qty", "uom": "uom", "net_rate": "rate", + "po_detail": "po_detail", }, + "postprocess": process_item_row, }, "E Invoice Trade Tax": { "doctype": "Purchase Taxes and Charges", @@ -520,3 +557,48 @@ def link_to_purchase_invoice(einvoice: str, purchase_invoice: str): frappe.throw(_("E Invoice Import {0} does not exist").format(einvoice)) pi.db_set("e_invoice_import", einvoice) + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def po_item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): + item_code = filters.pop("item_code") + purchase_order = filters.pop("parent") + + if not purchase_order: + return [] + + purchase_order = frappe.get_cached_doc("Purchase Order", purchase_order) + purchase_order.check_permission("read") + + results = [ + [ + row.name, + _("Row {0}").format(row.idx), + row.item_code, + row.description[:100] + "..." if len(row.description) > 40 else row.description, + row.get_formatted("qty") + " " + row.uom, + row.get_formatted("net_rate") + " / " + row.uom, + ] + for row in purchase_order.items + if not item_code or row.item_code == item_code + ] + + if not txt: + return results + + return [row for row in results if txt in ", ".join(row)] + + +@frappe.whitelist() +def get_po_item_details(po_detail: str): + purchase_order_name = frappe.db.get_value("Purchase Order Item", po_detail, "parent") + purchase_order = frappe.get_cached_doc("Purchase Order", purchase_order_name) + if not purchase_order.has_permission("read"): + return {} + + row = purchase_order.getone("items", {"name": po_detail}) + return { + "item_code": row.item_code, + "uom": row.uom, + } diff --git a/eu_einvoice/european_e_invoice/doctype/e_invoice_item/e_invoice_item.json b/eu_einvoice/european_e_invoice/doctype/e_invoice_item/e_invoice_item.json index 8c9fad5..0281a76 100644 --- a/eu_einvoice/european_e_invoice/doctype/e_invoice_item/e_invoice_item.json +++ b/eu_einvoice/european_e_invoice/doctype/e_invoice_item/e_invoice_item.json @@ -13,6 +13,7 @@ "column_break_grml", "item", "create_item", + "po_detail", "delivery_section", "billed_quantity", "unit_code", @@ -126,12 +127,18 @@ "fieldtype": "Button", "label": "Create Item", "read_only_depends_on": "item" + }, + { + "fieldname": "po_detail", + "fieldtype": "Link", + "label": "Purchase Order Row", + "options": "Purchase Order Item" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-08-26 14:50:15.775645", + "modified": "2024-12-31 09:04:36.581784", "modified_by": "Administrator", "module": "European e-Invoice", "name": "E Invoice Item", diff --git a/eu_einvoice/european_e_invoice/doctype/e_invoice_item/e_invoice_item.py b/eu_einvoice/european_e_invoice/doctype/e_invoice_item/e_invoice_item.py index e0e5a89..540ed15 100644 --- a/eu_einvoice/european_e_invoice/doctype/e_invoice_item/e_invoice_item.py +++ b/eu_einvoice/european_e_invoice/doctype/e_invoice_item/e_invoice_item.py @@ -20,6 +20,7 @@ class EInvoiceItem(Document): parent: DF.Data parentfield: DF.Data parenttype: DF.Data + po_detail: DF.Link | None product_description: DF.SmallText | None product_name: DF.Data | None seller_product_id: DF.Data | None