From d26d3c32c6373077a1e6f1c2dd27ef81d929cc64 Mon Sep 17 00:00:00 2001 From: Emanuel Kagombora Date: Tue, 21 Apr 2026 19:03:45 +0300 Subject: [PATCH] feat(av_tools): add delivery exchange doctypes --- .../delivery_exchange_item/__init__.py | 0 .../delivery_exchange_item.js | 78 +++++++++++ .../delivery_exchange_item.json | 121 +++++++++++++++++ .../delivery_exchange_item.py | 123 ++++++++++++++++++ .../test_delivery_exchange_item.py | 9 ++ .../__init__.py | 0 .../delivery_exchange_item_details.json | 105 +++++++++++++++ .../delivery_exchange_item_details.py | 8 ++ .../__init__.py | 0 ...ivery_exchange_non_stock_item_details.json | 94 +++++++++++++ ...elivery_exchange_non_stock_item_details.py | 8 ++ av_tools/patches.txt | 1 + .../v1_0/move_delivery_exchange_doctypes.py | 14 ++ 13 files changed, 561 insertions(+) create mode 100644 av_tools/av_tools/doctype/delivery_exchange_item/__init__.py create mode 100644 av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.js create mode 100644 av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.json create mode 100644 av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.py create mode 100644 av_tools/av_tools/doctype/delivery_exchange_item/test_delivery_exchange_item.py create mode 100644 av_tools/av_tools/doctype/delivery_exchange_item_details/__init__.py create mode 100644 av_tools/av_tools/doctype/delivery_exchange_item_details/delivery_exchange_item_details.json create mode 100644 av_tools/av_tools/doctype/delivery_exchange_item_details/delivery_exchange_item_details.py create mode 100644 av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/__init__.py create mode 100644 av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/delivery_exchange_non_stock_item_details.json create mode 100644 av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/delivery_exchange_non_stock_item_details.py create mode 100644 av_tools/patches/v1_0/move_delivery_exchange_doctypes.py diff --git a/av_tools/av_tools/doctype/delivery_exchange_item/__init__.py b/av_tools/av_tools/doctype/delivery_exchange_item/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.js b/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.js new file mode 100644 index 0000000..496388a --- /dev/null +++ b/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.js @@ -0,0 +1,78 @@ +// Copyright (c) 2024, Aakvatech and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Delivery Exchange Item', { + refresh: (frm) => { + frm.set_query("item_exchange", "stock_items", function () { + var data = { + filters: { + 'is_stock_item': 1, + } + }; + return data + }); + frm.set_query("item_exchange", "non_stock_items", function () { + var data = { + filters: { + 'is_stock_item': 0, + } + }; + return data + }); + }, + + ref_docname: (frm) => { + if (frm.doc.ref_docname) { + fetch_item_details(frm) + } + } + +}); + +function fetch_item_details(frm) { + frappe.call({ + method: 'av_tools.av_tools.doctype.delivery_exchange_item.delivery_exchange_item.get_item_details', + args: { + 'doctype': frm.doc.ref_doctype, + 'doctype_id': frm.doc.ref_docname, + 'packed': false, + // 'item_code': row.item_sold_or_delivered, + }, + callback: function (r) { + if (!r.exc) { + console.log(r.message); + frm.doc.stock_items = [] + frm.doc.non_stock_items = [] + frm.refresh_field("stock_items"); + frm.refresh_field("non_stock_items"); + for (let d of r.message) { + if (d.is_stock_item) { + let row = frm.add_child("stock_items", { + "item_sold_or_delivered": d.item_code, + "amount_exchange": d.amount, + "warehouse": d.warehouse, + "qty_sold_or_delivered": d.qty, + "rate_sold_or_delivered": d.rate, + "uom": d.uom, + "amount_sold_or_delivered": d.amount, + }); + } else { + let row = frm.add_child("non_stock_items", { + "item_sold_or_delivered": d.item_code, + "amount_exchange": d.amount, + "warehouse": d.warehouse, + "qty_sold_or_delivered": d.qty, + "rate_sold_or_delivered": d.rate, + "uom": d.uom, + "amount_sold_or_delivered": d.amount, + }); + } + + frm.refresh_field("stock_items"); + frm.refresh_field("non_stock_items"); + } + } + } + }); +} + diff --git a/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.json b/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.json new file mode 100644 index 0000000..e3af29f --- /dev/null +++ b/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.json @@ -0,0 +1,121 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "format:DEI-{YYYY}-{####}", + "creation": "2024-01-02 11:08:07.241357", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "section_break_5c7ku", + "ref_doctype", + "column_break_4tsfy", + "ref_docname", + "section_break_cslzt", + "stock_items", + "non_stock_items", + "section_break_ade0i", + "remarks", + "column_break_qw1gs", + "amended_from" + ], + "fields": [ + { + "fieldname": "column_break_4tsfy", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_cslzt", + "fieldtype": "Section Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "section_break_5c7ku", + "fieldtype": "Section Break" + }, + { + "fieldname": "remarks", + "fieldtype": "Text", + "label": "Remarks", + "max_height": "60px" + }, + { + "fieldname": "column_break_qw1gs", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_ade0i", + "fieldtype": "Section Break" + }, + { + "fieldname": "stock_items", + "fieldtype": "Table", + "label": "Stock Items", + "options": "Delivery Exchange Item Details" + }, + { + "fieldname": "non_stock_items", + "fieldtype": "Table", + "label": "Non Stock Items", + "options": "Delivery Exchange Non Stock Item Details" + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Delivery Exchange Item", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "ref_doctype", + "fieldtype": "Select", + "label": "Reference Doctype", + "options": "Sales Invoice\nDelivery Note" + }, + { + "fieldname": "ref_docname", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Docname", + "options": "ref_doctype", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "links": [], + "modified": "2024-09-23 12:38:40.465860", + "modified_by": "Administrator", + "module": "Av Tools", + "name": "Delivery Exchange Item", + "naming_rule": "Expression", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} diff --git a/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.py b/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.py new file mode 100644 index 0000000..b763976 --- /dev/null +++ b/av_tools/av_tools/doctype/delivery_exchange_item/delivery_exchange_item.py @@ -0,0 +1,123 @@ +# Copyright (c) 2024, Aakvatech and contributors +# For license information, please see license.txt + +import frappe +from frappe.model.document import Document +from frappe.utils import flt, today + + +class DeliveryExchangeItem(Document): + def validate(self): + msg = "" + + for index, row in enumerate(self.stock_items): + table_name = self.stock_items[0].parentfield.replace("_", " ").title() + validate = get_item_details( + self.ref_doctype, self.ref_docname, item_code=row.item_sold_or_delivered + ) + if not validate: + msg += f"In table {table_name}, Row {index + 1}, Item {row.item_sold_or_delivered} does not exist in {self.ref_doctype} {self.ref_docname}
" + item_exchange_rate = frappe.db.get_value( + "Item Price", + { + "price_list": validate[0].selling_price_list, + "item_code": row.item_exchange, + }, + "price_list_rate", + ) + if (flt(item_exchange_rate) * flt(row.qty_sold_or_delivered)) != flt( + row.amount_sold_or_delivered + ): + msg += f"In table {table_name}, Row {index + 1}, Amounts for Item {row.item_sold_or_delivered} and {row.item_exchange} do not match at selling rate {validate[0].selling_price_list}" + + for index, row in enumerate(self.non_stock_items): + table_name = self.non_stock_items[0].parentfield.replace("_", " ").title() + validate = get_item_details( + self.ref_doctype, self.ref_docname, item_code=row.item_sold_or_delivered + ) + if not validate: + msg += f"In table {table_name}, Row {index + 1}, Item {row.item_sold_or_delivered} does not exist in {self.ref_doctype} {self.ref_docname}
" + item_exchange_rate = frappe.db.get_value( + "Item Price", + { + "price_list": validate[0].selling_price_list, + "item_code": row.item_exchange, + }, + "price_list_rate", + ) + if (flt(item_exchange_rate) * flt(row.qty_sold_or_delivered)) != flt( + row.amount_sold_or_delivered + ): + msg += f"In table {table_name}, Row {index + 1}, Amounts for Item {row.item_sold_or_delivered} and {row.item_exchange} do not match at selling rate {validate[0].selling_price_list}" + + if msg: + frappe.throw(f"{msg}") + + def on_submit(self): + stock_items = [] + if len(self.stock_items) > 0: + for row in self.stock_items: + stock_items.append( + { + "s_warehouse": row.warehouse, + "t_warehouse": "", + "item_code": row.item_exchange, + "qty": row.qty_sold_or_delivered, + "basic_rate": row.rate_sold_or_delivered, + "uom": row.uom, + } + ) + stock_items.append( + { + "s_warehouse": "", + "t_warehouse": row.warehouse, + "item_code": row.item_sold_or_delivered, + "qty": row.qty_sold_or_delivered, + "basic_rate": row.rate_sold_or_delivered, + "set_basic_rate_manually": 1, + "uom": row.uom, + "basic_amount": row.amount_sold_or_delivered, + } + ) + doc = frappe.get_doc( + { + "doctype": "Stock Entry", + "company": self.company, + "stock_entry_type": "Delivery Exchange", + "posting_date": today(), + "items": stock_items, + # "ref_doctype": self.doctype, + # "ref_docname": self.name, + } + ) + doc.insert(ignore_permissions=True) + + +@frappe.whitelist() +def get_item_details(doctype, doctype_id, item_code=None): + condition = "" + if item_code: + condition += f"AND cdt.item_code = '{item_code}'" + + return frappe.db.sql( + f""" + SELECT + cdt.item_code, + cdt.amount, + cdt.warehouse, + cdt.qty, + cdt.rate, + cdt.uom, + pdt.selling_price_list, + i.is_stock_item + FROM `tab{doctype}` pdt + INNER JOIN `tab{doctype} Item` cdt + ON pdt.name = cdt.parent + INNER JOIN `tabItem` i + ON i.name = cdt.item_code + WHERE pdt.name = '{doctype_id}' + + {condition} + """, + as_dict=1, + ) diff --git a/av_tools/av_tools/doctype/delivery_exchange_item/test_delivery_exchange_item.py b/av_tools/av_tools/doctype/delivery_exchange_item/test_delivery_exchange_item.py new file mode 100644 index 0000000..931c9ab --- /dev/null +++ b/av_tools/av_tools/doctype/delivery_exchange_item/test_delivery_exchange_item.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Aakvatech and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestDeliveryExchangeItem(FrappeTestCase): + pass diff --git a/av_tools/av_tools/doctype/delivery_exchange_item_details/__init__.py b/av_tools/av_tools/doctype/delivery_exchange_item_details/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/av_tools/av_tools/doctype/delivery_exchange_item_details/delivery_exchange_item_details.json b/av_tools/av_tools/doctype/delivery_exchange_item_details/delivery_exchange_item_details.json new file mode 100644 index 0000000..254adfb --- /dev/null +++ b/av_tools/av_tools/doctype/delivery_exchange_item_details/delivery_exchange_item_details.json @@ -0,0 +1,105 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-01-02 11:18:19.135187", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_sold_or_delivered", + "rate_sold_or_delivered", + "qty_sold_or_delivered", + "amount_sold_or_delivered", + "warehouse", + "column_break_frc89", + "item_exchange", + "amount_exchange", + "uom", + "amended_from" + ], + "fields": [ + { + "fieldname": "item_sold_or_delivered", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Sold/Delivered", + "options": "Item", + "read_only": 1 + }, + { + "fieldname": "amount_sold_or_delivered", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Amount Sold/Delivered ", + "read_only": 1 + }, + { + "fieldname": "item_exchange", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Exchange", + "options": "Item", + "reqd": 1 + }, + { + "fieldname": "amount_exchange", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Amount Exchange", + "read_only": 1 + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse", + "read_only": 1 + }, + { + "fieldname": "rate_sold_or_delivered", + "fieldtype": "Data", + "label": "Rate Sold/Delivered", + "read_only": 1 + }, + { + "fieldname": "qty_sold_or_delivered", + "fieldtype": "Data", + "label": "Qty Sold/Delivered", + "read_only": 1 + }, + { + "fieldname": "column_break_frc89", + "fieldtype": "Column Break" + }, + { + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Delivery Exchange Item Details", + "print_hide": 1, + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "is_submittable": 1, + "istable": 1, + "links": [], + "modified": "2024-01-08 17:30:11.929988", + "modified_by": "Administrator", + "module": "Av Tools", + "name": "Delivery Exchange Item Details", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} diff --git a/av_tools/av_tools/doctype/delivery_exchange_item_details/delivery_exchange_item_details.py b/av_tools/av_tools/doctype/delivery_exchange_item_details/delivery_exchange_item_details.py new file mode 100644 index 0000000..5100aca --- /dev/null +++ b/av_tools/av_tools/doctype/delivery_exchange_item_details/delivery_exchange_item_details.py @@ -0,0 +1,8 @@ +# Copyright (c) 2024, Aakvatech and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class DeliveryExchangeItemDetails(Document): + pass diff --git a/av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/__init__.py b/av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/delivery_exchange_non_stock_item_details.json b/av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/delivery_exchange_non_stock_item_details.json new file mode 100644 index 0000000..d53e4d6 --- /dev/null +++ b/av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/delivery_exchange_non_stock_item_details.json @@ -0,0 +1,94 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-01-08 12:21:31.843374", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_sold_or_delivered", + "rate_sold_or_delivered", + "qty_sold_or_delivered", + "amount_sold_or_delivered", + "warehouse", + "column_break_frc89", + "item_exchange", + "amount_exchange", + "uom" + ], + "fields": [ + { + "fieldname": "item_sold_or_delivered", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Sold/Delivered", + "options": "Item", + "read_only": 1 + }, + { + "fieldname": "rate_sold_or_delivered", + "fieldtype": "Data", + "label": "Rate Sold/Delivered", + "read_only": 1 + }, + { + "fieldname": "qty_sold_or_delivered", + "fieldtype": "Data", + "label": "Qty Sold/Delivered", + "read_only": 1 + }, + { + "fieldname": "amount_sold_or_delivered", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Amount Sold/Delivered ", + "read_only": 1 + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse", + "read_only": 1 + }, + { + "fieldname": "column_break_frc89", + "fieldtype": "Column Break" + }, + { + "fieldname": "item_exchange", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Exchange", + "options": "Item", + "reqd": 1 + }, + { + "fieldname": "amount_exchange", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Amount Exchange", + "read_only": 1 + }, + { + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-01-08 15:47:56.471503", + "modified_by": "Administrator", + "module": "Av Tools", + "name": "Delivery Exchange Non Stock Item Details", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} diff --git a/av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/delivery_exchange_non_stock_item_details.py b/av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/delivery_exchange_non_stock_item_details.py new file mode 100644 index 0000000..b547961 --- /dev/null +++ b/av_tools/av_tools/doctype/delivery_exchange_non_stock_item_details/delivery_exchange_non_stock_item_details.py @@ -0,0 +1,8 @@ +# Copyright (c) 2024, Aakvatech and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class DeliveryExchangeNonStockItemDetails(Document): + pass diff --git a/av_tools/patches.txt b/av_tools/patches.txt index 214b374..e40c9fe 100644 --- a/av_tools/patches.txt +++ b/av_tools/patches.txt @@ -11,3 +11,4 @@ av_tools.patches.v1_0.migrate_generic_erp_behavior_overrides av_tools.patches.v1_0.migrate_ai_integration_site_data av_tools.patches.v1_0.migrate_generic_technical_admin_reports av_tools.patches.v1_0.migrate_report_extension_site_data +av_tools.patches.v1_0.move_delivery_exchange_doctypes diff --git a/av_tools/patches/v1_0/move_delivery_exchange_doctypes.py b/av_tools/patches/v1_0/move_delivery_exchange_doctypes.py new file mode 100644 index 0000000..f070a7d --- /dev/null +++ b/av_tools/patches/v1_0/move_delivery_exchange_doctypes.py @@ -0,0 +1,14 @@ +import frappe + + +DELIVERY_EXCHANGE_DOCTYPES = ( + "Delivery Exchange Item", + "Delivery Exchange Item Details", + "Delivery Exchange Non Stock Item Details", +) + + +def execute(): + for doctype in DELIVERY_EXCHANGE_DOCTYPES: + if frappe.db.exists("DocType", doctype): + frappe.db.set_value("DocType", doctype, "module", "Av Tools")