From cb6494876fe780c1900174a9f11957bbe8c88a8d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 27 Mar 2021 22:21:41 +0530 Subject: [PATCH 1/2] feat: purchase receipt creation from purchase invoice --- .../purchase_invoice/purchase_invoice.js | 30 +++++ .../purchase_invoice/purchase_invoice.json | 12 +- .../purchase_invoice/purchase_invoice.py | 38 +++++++ .../purchase_invoice_item.json | 3 +- .../billed_items_to_be_received/__init__.py | 0 .../billed_items_to_be_received.js | 29 +++++ .../billed_items_to_be_received.json | 39 +++++++ .../billed_items_to_be_received.py | 107 ++++++++++++++++++ .../purchase_receipt/purchase_receipt.js | 28 +++++ .../purchase_receipt/purchase_receipt.py | 17 ++- .../purchase_receipt_item.json | 24 +++- 11 files changed, 320 insertions(+), 7 deletions(-) create mode 100644 erpnext/accounts/report/billed_items_to_be_received/__init__.py create mode 100644 erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js create mode 100644 erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.json create mode 100644 erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 66a8e206a81..73475a1131d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -523,6 +523,28 @@ frappe.ui.form.on("Purchase Invoice", { } }, + refresh: function(frm) { + frm.events.add_custom_buttons(frm); + }, + + add_custom_buttons: function(frm) { + if (frm.doc.per_received < 100) { + frm.add_custom_button(__('Purchase Receipt'), () => { + frm.events.make_purchase_receipt(frm); + }, __('Create')); + } + + if (frm.doc.docstatus == 1 && frm.doc.per_received > 0) { + frm.add_custom_button(__('Purchase Receipt'), () => { + frappe.route_options = { + 'purchase_invoice': frm.doc.name + } + + frappe.set_route("List", "Purchase Receipt", "List") + }, __('View')); + } + }, + onload: function(frm) { if(frm.doc.__onload && frm.is_new()) { if(frm.doc.supplier) { @@ -548,5 +570,13 @@ frappe.ui.form.on("Purchase Invoice", { update_stock: function(frm) { hide_fields(frm.doc); frm.fields_dict.items.grid.toggle_reqd("item_code", frm.doc.update_stock? true: false); + }, + + make_purchase_receipt: function(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_purchase_receipt", + frm: frm, + freeze_message: __("Creating Purchase Receipt ...") + }) } }) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 739bd671f79..5c37eda8d46 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -163,7 +163,8 @@ "to_date", "column_break_114", "auto_repeat", - "update_auto_repeat_reference" + "update_auto_repeat_reference", + "per_received" ], "fields": [ { @@ -1364,6 +1365,15 @@ "print_hide": 1, "print_width": "50px", "width": "50px" + }, + { + "fieldname": "per_received", + "fieldtype": "Percent", + "hidden": 1, + "label": "Per Received", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-file-text", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 5c4e32e493e..7d008507a2b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1207,3 +1207,41 @@ def make_inter_company_sales_invoice(source_name, target_doc=None): def on_doctype_update(): frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"]) + +@frappe.whitelist() +def make_purchase_receipt(source_name, target_doc=None): + def update_item(obj, target, source_parent): + target.qty = flt(obj.qty) - flt(obj.received_qty) + target.received_qty = flt(obj.qty) - flt(obj.received_qty) + target.stock_qty = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.conversion_factor) + target.amount = (flt(obj.qty) - flt(obj.received_qty)) * flt(obj.rate) + target.base_amount = (flt(obj.qty) - flt(obj.received_qty)) * \ + flt(obj.rate) * flt(source_parent.conversion_rate) + + doc = get_mapped_doc("Purchase Invoice", source_name, { + "Purchase Invoice": { + "doctype": "Purchase Receipt", + "validation": { + "docstatus": ["=", 1], + } + }, + "Purchase Invoice Item": { + "doctype": "Purchase Receipt Item", + "field_map": { + "name": "purchase_invoice_item", + "parent": "purchase_invoice", + "bom": "bom", + "purchase_order": "purchase_order", + "po_detail": "purchase_order_item", + "material_request": "material_request", + "material_request_item": "material_request_item" + }, + "postprocess": update_item, + "condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) + }, + "Purchase Taxes and Charges": { + "doctype": "Purchase Taxes and Charges" + } + }, target_doc) + + return doc diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 96ad0fd7852..10e1c73ea90 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -607,6 +607,7 @@ "oldfieldname": "purchase_order", "oldfieldtype": "Link", "options": "Purchase Order", + "print_hide": 1, "read_only": 1, "search_index": 1 }, @@ -853,7 +854,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-02-23 00:59:52.614805", + "modified": "2021-03-30 09:02:39.256602", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/report/billed_items_to_be_received/__init__.py b/erpnext/accounts/report/billed_items_to_be_received/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js new file mode 100644 index 00000000000..e1fccb6e720 --- /dev/null +++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js @@ -0,0 +1,29 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports['Billed Items To Be Received'] = { + 'filters': [ + { + 'label': __('Company'), + 'fieldname': 'company', + 'fieldtype': 'Link', + 'options': 'Company', + 'reqd': 1, + 'default': frappe.defaults.get_default('Company') + }, + { + 'label': __('As on Date'), + 'fieldname': 'posting_date', + 'fieldtype': 'Date', + 'reqd': 1, + 'default': get_today() + }, + { + 'label': __('Purchase Invoice'), + 'fieldname': 'purchase_invoice', + 'fieldtype': 'Link', + 'options': 'Purchase Invoice' + } + ] +}; diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.json b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.json new file mode 100644 index 00000000000..de09b33c960 --- /dev/null +++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.json @@ -0,0 +1,39 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2021-03-30 09:35:38.683028", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2021-03-31 08:48:30.944429", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Billed Items To Be Received", + "owner": "Administrator", + "prepared_report": 0, + "query": "", + "ref_doctype": "Purchase Invoice", + "report_name": "Billed Items To Be Received", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Purchase User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + }, + { + "role": "Stock User" + } + ] +} \ No newline at end of file diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py new file mode 100644 index 00000000000..2ce5d50edf4 --- /dev/null +++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py @@ -0,0 +1,107 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +def execute(filters=None): + data = get_data(filters) or [] + columns = get_columns() + + return columns, data + +def get_data(report_filters): + filters = get_report_filters(report_filters) + fields = get_report_fields() + + return frappe.get_all('Purchase Invoice', + fields= fields, filters=filters) + +def get_report_filters(report_filters): + filters = [['Purchase Invoice','company','=',report_filters.get('company')], + ['Purchase Invoice','posting_date','<=',report_filters.get('posting_date')], ['Purchase Invoice','docstatus','=',1], + ['Purchase Invoice','per_received','<',100], ['Purchase Invoice','update_stock','=',0]] + + if report_filters.get('purchase_invoice'): + filters.append(['Purchase Invoice','per_received','in',[report_filters.get('purchase_invoice')]]) + + return filters + +def get_report_fields(): + fields = [] + for p_field in ['name', 'supplier', 'company', 'posting_date', 'currency']: + fields.append('`tabPurchase Invoice`.`{}`'.format(p_field)) + + for c_field in ['item_code', 'item_name', 'uom', 'qty', 'received_qty', 'rate', 'amount']: + fields.append('`tabPurchase Invoice Item`.`{}`'.format(c_field)) + + return fields + +def get_columns(): + return [ + { + 'label': _('Purchase Invoice'), + 'fieldname': 'name', + 'fieldtype': 'Link', + 'options': 'Purchase Invoice', + 'width': 170 + }, + { + 'label': _('Supplier'), + 'fieldname': 'supplier', + 'fieldtype': 'Link', + 'options': 'Supplier', + 'width': 120 + }, + { + 'label': _('Posting Date'), + 'fieldname': 'posting_date', + 'fieldtype': 'Date', + 'width': 100 + }, + { + 'label': _('Item Code'), + 'fieldname': 'item_code', + 'fieldtype': 'Link', + 'options': 'Item', + 'width': 100 + }, + { + 'label': _('Item Name'), + 'fieldname': 'item_name', + 'fieldtype': 'Data', + 'width': 100 + }, + { + 'label': _('UOM'), + 'fieldname': 'uom', + 'fieldtype': 'Link', + 'options': 'UOM', + 'width': 100 + }, + { + 'label': _('Invoiced Qty'), + 'fieldname': 'qty', + 'fieldtype': 'Float', + 'width': 100 + }, + { + 'label': _('Received Qty'), + 'fieldname': 'received_qty', + 'fieldtype': 'Float', + 'width': 100 + }, + { + 'label': _('Rate'), + 'fieldname': 'rate', + 'fieldtype': 'Currency', + 'width': 100 + }, + { + 'label': _('Amount'), + 'fieldname': 'amount', + 'fieldtype': 'Currency', + 'width': 100 + } + ] \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 57cc3504a90..f9abea64532 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -73,6 +73,34 @@ frappe.ui.form.on("Purchase Receipt", { }) }, __('Create')); } + + frm.events.add_custom_buttons(frm); + }, + + add_custom_buttons: function(frm) { + if (frm.doc.docstatus == 0) { + frm.add_custom_button(__('Purchase Invoice'), function () { + if (!frm.doc.supplier) { + frappe.throw({ + title: __("Mandatory"), + message: __("Please Select a Supplier") + }); + } + erpnext.utils.map_current_doc({ + method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_purchase_receipt", + source_doctype: "Purchase Invoice", + target: frm, + setters: { + supplier: frm.doc.supplier, + }, + get_query_filters: { + docstatus: 1, + per_received: ["<", 100], + company: frm.doc.company + } + }) + }, __("Get Items From")); + } }, company: function(frm) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 5d7597b2db5..b4cf659bf3b 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -53,7 +53,20 @@ class PurchaseReceipt(BuyingController): 'target_ref_field': 'stock_qty', 'source_field': 'stock_qty', 'percent_join_field': 'material_request' + }, + { + 'source_dt': 'Purchase Receipt Item', + 'target_dt': 'Purchase Invoice Item', + 'join_field': 'purchase_invoice_item', + 'target_field': 'received_qty', + 'target_parent_dt': 'Purchase Invoice', + 'target_parent_field': 'per_received', + 'target_ref_field': 'qty', + 'source_field': 'received_qty', + 'percent_join_field': 'purchase_invoice', + 'overflow_type': 'receipt' }] + if cint(self.is_return): self.status_updater.extend([ { @@ -513,7 +526,9 @@ class PurchaseReceipt(BuyingController): def update_billing_status(self, update_modified=True): updated_pr = [self.name] for d in self.get("items"): - if d.purchase_order_item: + if d.purchase_invoice and d.purchase_invoice_item: + d.db_set('billed_amt', d.amount, update_modified=update_modified) + elif d.purchase_order_item: updated_pr += update_billed_amount_based_on_po(d.purchase_order_item, update_modified) for pr in set(updated_pr): diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index efe3642d23c..82cc98e7f75 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -72,16 +72,18 @@ "warehouse", "rejected_warehouse", "from_warehouse", - "purchase_order", "material_request", + "purchase_order", + "purchase_invoice", "column_break_40", "is_fixed_asset", "asset_location", "asset_category", "schedule_date", "quality_inspection", - "purchase_order_item", "material_request_item", + "purchase_order_item", + "purchase_invoice_item", "purchase_receipt_item", "delivery_note_item", "putaway_rule", @@ -937,7 +939,21 @@ "fieldname": "base_rate_with_margin", "fieldtype": "Currency", "label": "Rate With Margin (Company Currency)", - "options": "Company:company:default_currency", + "options": "Company:company:default_currency" + }, + { + "fieldname": "purchase_invoice", + "fieldtype": "Link", + "label": "Purchase Invoice", + "options": "Purchase Invoice", + "read_only": 1 + }, + { + "fieldname": "purchase_invoice_item", + "fieldtype": "Data", + "hidden": 1, + "label": "Purchase Invoice Item", + "no_copy": 1, "print_hide": 1, "read_only": 1 } @@ -945,7 +961,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-02-23 00:59:14.360847", + "modified": "2021-03-29 04:17:00.336298", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From a5d062453ecf6266d4e420e343bea88f1e54d96c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 8 Apr 2021 22:20:10 +0530 Subject: [PATCH 2/2] feat: added test case --- .../purchase_invoice/purchase_invoice.py | 2 +- .../purchase_order/test_purchase_order.py | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 7d008507a2b..83e9f7583e9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1218,7 +1218,7 @@ def make_purchase_receipt(source_name, target_doc=None): target.base_amount = (flt(obj.qty) - flt(obj.received_qty)) * \ flt(obj.rate) * flt(source_parent.conversion_rate) - doc = get_mapped_doc("Purchase Invoice", source_name, { + doc = get_mapped_doc("Purchase Invoice", source_name, { "Purchase Invoice": { "doctype": "Purchase Receipt", "validation": { diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 3c4f908ee4f..42f4472f29e 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -435,6 +435,35 @@ class TestPurchaseOrder(unittest.TestCase): po.load_from_db() self.assertEqual(po.get("items")[0].received_qty, 5) + def test_purchase_order_invoice_receipt_workflow(self): + from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_purchase_receipt + + po = create_purchase_order() + pi = make_pi_from_po(po.name) + + pi.submit() + + pr = make_purchase_receipt(pi.name) + pr.submit() + + pi.load_from_db() + + self.assertEquals(pi.per_received, 100.00) + self.assertEquals(pi.items[0].qty, pi.items[0].received_qty) + + po.load_from_db() + + self.assertEquals(po.per_received, 100.00) + self.assertEquals(po.per_billed, 100.00) + + pr.cancel() + + pi.load_from_db() + pi.cancel() + + po.load_from_db() + po.cancel() + def test_make_purchase_invoice(self): po = create_purchase_order(do_not_submit=True)