From 39e2c2bb097a8c93efd7417c348d17ead63c9ad7 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Mon, 25 Jan 2016 17:03:50 +0530 Subject: [PATCH] [fix] obey no_copy while creating recurring documents --- .../purchase_invoice/purchase_invoice.json | 6 +-- .../purchase_invoice/purchase_invoice.py | 7 ++- .../doctype/sales_invoice/sales_invoice.py | 10 +++- .../doctype/purchase_order/purchase_order.py | 23 +++------ .../purchase_order/test_purchase_order.py | 34 ++++++------- .../purchase_order_item.json | 5 +- erpnext/controllers/accounts_controller.py | 6 --- erpnext/controllers/recurring_document.py | 51 ++++++++++--------- .../doctype/sales_order/sales_order.py | 23 ++++----- 9 files changed, 79 insertions(+), 86 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 92fee00ce2..da28767082 100755 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1563,7 +1563,7 @@ "in_list_view": 0, "label": "Write Off Account", "length": 0, - "no_copy": 1, + "no_copy": 0, "options": "Account", "permlevel": 0, "print_hide": 1, @@ -1588,7 +1588,7 @@ "in_list_view": 0, "label": "Write Off Cost Center", "length": 0, - "no_copy": 1, + "no_copy": 0, "options": "Cost Center", "permlevel": 0, "print_hide": 1, @@ -2487,7 +2487,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2015-12-17 16:18:58.177334", + "modified": "2016-01-25 05:18:57.728258", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 5d91426b22..6d61abe4cc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -263,9 +263,9 @@ class PurchaseInvoice(BuyingController): # parent's gl entry if self.grand_total: # Didnot use base_grand_total to book rounding loss gle - grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate, + grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate, self.precision("grand_total")) - + gl_entries.append( self.get_gl_dict({ "account": self.credit_to, @@ -454,6 +454,9 @@ class PurchaseInvoice(BuyingController): for pr in set(updated_pr): frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(update_modified=update_modified) + def on_recurring(self, reference_doc): + self.due_date = None + @frappe.whitelist() def get_expense_account(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index e87794a979..3c60bfbb70 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -532,9 +532,9 @@ class SalesInvoice(SellingController): def make_customer_gl_entry(self, gl_entries): if self.grand_total: # Didnot use base_grand_total to book rounding loss gle - grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate, + grand_total_in_company_currency = flt(self.grand_total * self.conversion_rate, self.precision("grand_total")) - + gl_entries.append( self.get_gl_dict({ "account": self.debit_to, @@ -656,6 +656,12 @@ class SalesInvoice(SellingController): for dn in set(updated_delivery_notes): frappe.get_doc("Delivery Note", dn).update_billing_percentage(update_modified=update_modified) + def on_recurring(self, reference_doc): + for fieldname in ("c_form_applicable", "c_form_no", "write_off_amount"): + self.set(fieldname, reference_doc.get(fieldname)) + + self.due_date = None + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f68935f9dc..caefe53ac3 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -84,11 +84,11 @@ class PurchaseOrder(BuyingController): if d.prevdoc_detail_docname and not d.schedule_date: d.schedule_date = frappe.db.get_value("Material Request Item", d.prevdoc_detail_docname, "schedule_date") - + def get_last_purchase_rate(self): """get last purchase rates for all items""" - + conversion_rate = flt(self.get('conversion_rate')) or 1.0 for d in self.get("items"): @@ -96,7 +96,7 @@ class PurchaseOrder(BuyingController): last_purchase_details = get_last_purchase_details(d.item_code, self.name) if last_purchase_details: - d.base_price_list_rate = (last_purchase_details['base_price_list_rate'] * + d.base_price_list_rate = (last_purchase_details['base_price_list_rate'] * (flt(d.conversion_factor) or 1.0)) d.discount_percentage = last_purchase_details['discount_percentage'] d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0) @@ -104,7 +104,7 @@ class PurchaseOrder(BuyingController): d.rate = d.base_rate / conversion_rate else: # if no last purchase found, reset all values to 0 - for field in ("base_price_list_rate", "base_rate", + for field in ("base_price_list_rate", "base_rate", "price_list_rate", "rate", "discount_percentage"): d.set(field, 0) @@ -188,7 +188,7 @@ class PurchaseOrder(BuyingController): def on_cancel(self): if self.is_against_so(): self.update_status_updater() - + if self.has_drop_ship_item(): self.update_delivered_qty_in_sales_order() @@ -219,17 +219,6 @@ class PurchaseOrder(BuyingController): def on_update(self): pass - def before_recurring(self): - super(PurchaseOrder, self).before_recurring() - - for field in ("per_received", "per_billed"): - self.set(field, None) - - for d in self.get("items"): - for field in ("received_qty", "billed_amt", "prevdoc_doctype", "prevdoc_docname", - "prevdoc_detail_docname", "supplier_quotation", "supplier_quotation_item"): - d.set(field, None) - def update_status_updater(self): self.status_updater[0].update({ "target_parent_dt": "Sales Order", @@ -254,7 +243,7 @@ class PurchaseOrder(BuyingController): def has_drop_ship_item(self): return any([d.delivered_by_supplier for d in self.items]) - + def is_against_so(self): return any([d.prevdoc_doctype for d in self.items if d.prevdoc_doctype=="Sales Order"]) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 1988a6d8e1..94dd07067d 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -19,16 +19,16 @@ class TestPurchaseOrder(unittest.TestCase): def test_ordered_qty(self): existing_ordered_qty = get_ordered_qty() - + po = create_purchase_order(do_not_submit=True) self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name) - + po.submit() self.assertEqual(get_ordered_qty(), existing_ordered_qty + 10) - create_pr_against_po(po.name) + create_pr_against_po(po.name) self.assertEqual(get_ordered_qty(), existing_ordered_qty + 6) - + po.load_from_db() self.assertEquals(po.get("items")[0].received_qty, 4) @@ -36,13 +36,13 @@ class TestPurchaseOrder(unittest.TestCase): pr = create_pr_against_po(po.name, received_qty=8) self.assertEqual(get_ordered_qty(), existing_ordered_qty) - + po.load_from_db() self.assertEquals(po.get("items")[0].received_qty, 12) pr.cancel() self.assertEqual(get_ordered_qty(), existing_ordered_qty + 6) - + po.load_from_db() self.assertEquals(po.get("items")[0].received_qty, 4) @@ -70,19 +70,19 @@ class TestPurchaseOrder(unittest.TestCase): from erpnext.utilities.transaction_base import UOMMustBeIntegerError po = create_purchase_order(qty=3.4, do_not_save=True) self.assertRaises(UOMMustBeIntegerError, po.insert) - - def test_ordered_qty_for_closing_po(self): - bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, - fields=["ordered_qty"]) - + + def test_ordered_qty_for_closing_po(self): + bin = frappe.get_all("Bin", filters={"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, + fields=["ordered_qty"]) + existing_ordered_qty = bin[0].ordered_qty if bin else 0.0 - + po = create_purchase_order(item_code= "_Test Item", qty=1) - + self.assertEquals(get_ordered_qty(item_code= "_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty+1) - + po.update_status("Closed") - + self.assertEquals(get_ordered_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"), existing_ordered_qty) def create_purchase_order(**args): @@ -108,9 +108,9 @@ def create_purchase_order(**args): po.insert() if not args.do_not_submit: po.submit() - + return po - + def create_pr_against_po(po, received_qty=4): pr = make_purchase_receipt(po) pr.get("items")[0].qty = received_qty diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 0d08796865..84afbe0c74 100755 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -7,6 +7,7 @@ "custom": 0, "docstatus": 0, "doctype": "DocType", + "document_type": "Document", "fields": [ { "allow_on_submit": 0, @@ -1155,7 +1156,7 @@ "in_list_view": 0, "label": "BOM", "length": 0, - "no_copy": 1, + "no_copy": 0, "options": "BOM", "permlevel": 0, "precision": "", @@ -1330,7 +1331,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-01-06 02:21:10.407871", + "modified": "2016-01-25 05:39:52.405200", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 5c84112df4..9aab8eb376 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -60,12 +60,6 @@ class AccountsController(TransactionBase): validate_recurring_document(self) convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date")) - def before_recurring(self): - if self.meta.get_field("fiscal_year"): - self.fiscal_year = None - if self.meta.get_field("due_date"): - self.due_date = None - def set_missing_values(self, for_validate=False): for fieldname in ["posting_date", "transaction_date"]: if not self.get(fieldname) and self.meta.get_field(fieldname): diff --git a/erpnext/controllers/recurring_document.py b/erpnext/controllers/recurring_document.py index d34002bbd4..013a70e0ac 100644 --- a/erpnext/controllers/recurring_document.py +++ b/erpnext/controllers/recurring_document.py @@ -47,12 +47,9 @@ def manage_recurring_documents(doctype, next_date=None, commit=True): where %s=%s and recurring_id=%s and docstatus=1""" % (doctype, date_field, '%s', '%s'), (next_date, recurring_id)): try: - ref_wrapper = frappe.get_doc(doctype, ref_document) - if hasattr(ref_wrapper, "before_recurring"): - ref_wrapper.before_recurring() - - new_document_wrapper = make_new_document(ref_wrapper, date_field, next_date) - send_notification(new_document_wrapper) + reference_doc = frappe.get_doc(doctype, ref_document) + new_doc = make_new_document(reference_doc, date_field, next_date) + send_notification(new_doc) if commit: frappe.db.commit() except: @@ -63,8 +60,8 @@ def manage_recurring_documents(doctype, next_date=None, commit=True): frappe.db.sql("update `tab%s` \ set is_recurring = 0 where name = %s" % (doctype, '%s'), (ref_document)) - notify_errors(ref_document, doctype, ref_wrapper.get("customer") or ref_wrapper.get("supplier"), - ref_wrapper.owner) + notify_errors(ref_document, doctype, reference_doc.get("customer") or reference_doc.get("supplier"), + reference_doc.owner) frappe.db.commit() exception_list.append(frappe.get_traceback()) @@ -76,34 +73,42 @@ def manage_recurring_documents(doctype, next_date=None, commit=True): exception_message = "\n\n".join([cstr(d) for d in exception_list]) frappe.throw(exception_message) -def make_new_document(ref_wrapper, date_field, posting_date): +def make_new_document(reference_doc, date_field, posting_date): from erpnext.accounts.utils import get_fiscal_year - new_document = frappe.copy_doc(ref_wrapper) - mcount = month_map[ref_wrapper.recurring_type] + new_document = frappe.copy_doc(reference_doc, ignore_no_copy=True) + mcount = month_map[reference_doc.recurring_type] - from_date = get_next_date(ref_wrapper.from_date, mcount) + from_date = get_next_date(reference_doc.from_date, mcount) # get last day of the month to maintain period if the from date is first day of its own month # and to date is the last day of its own month - if (cstr(get_first_day(ref_wrapper.from_date)) == cstr(ref_wrapper.from_date)) and \ - (cstr(get_last_day(ref_wrapper.to_date)) == cstr(ref_wrapper.to_date)): - to_date = get_last_day(get_next_date(ref_wrapper.to_date, mcount)) + if (cstr(get_first_day(reference_doc.from_date)) == cstr(reference_doc.from_date)) and \ + (cstr(get_last_day(reference_doc.to_date)) == cstr(reference_doc.to_date)): + to_date = get_last_day(get_next_date(reference_doc.to_date, mcount)) else: - to_date = get_next_date(ref_wrapper.to_date, mcount) + to_date = get_next_date(reference_doc.to_date, mcount) new_document.update({ date_field: posting_date, "from_date": from_date, "to_date": to_date, - "fiscal_year": get_fiscal_year(posting_date)[0], - "owner": ref_wrapper.owner, + "fiscal_year": get_fiscal_year(posting_date)[0] }) - if ref_wrapper.doctype == "Sales Order": - new_document.update({ - "delivery_date": get_next_date(ref_wrapper.delivery_date, mcount, - cint(ref_wrapper.repeat_on_day_of_month)) - }) + # copy document fields + for fieldname in ("owner", "recurring_type", "repeat_on_day_of_month", + "recurring_id", "notification_email_address", "is_recurring", "end_date", + "title", "naming_series", "select_print_heading", "ignore_pricing_rule", + "posting_time", "remarks"): + if new_document.meta.get_field(fieldname): + new_document.set(fieldname, reference_doc.get(fieldname)) + + # copy item fields + for i, item in enumerate(new_document.items): + for fieldname in ("page_break",): + item.set(fieldname, reference_doc.items[i].get(fieldname)) + + new_document.run_method("on_recurring", reference_doc=reference_doc) new_document.submit() diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 8bc96e666b..aafb0df278 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -10,6 +10,7 @@ from frappe import _ from frappe.model.mapper import get_mapped_doc from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty from frappe.desk.notifications import clear_doctype_notifications +from erpnext.controllers.recurring_document import month_map, get_next_date from erpnext.controllers.selling_controller import SellingController @@ -288,13 +289,13 @@ class SalesOrder(SellingController): frappe.db.set_value("Sales Order", self.name, "per_delivered", flt(delivered_qty/tot_qty) * 100, update_modified=False) - + def set_indicator(self): """Set indicator for portal""" if self.per_billed < 100 and self.per_delivered < 100: self.indicator_color = "orange" self.indicator_title = _("Not Paid and Not Delivered") - + elif self.per_billed == 100 and self.per_delivered < 100: self.indicator_color = "orange" self.indicator_title = _("Paid and Not Delivered") @@ -302,7 +303,12 @@ class SalesOrder(SellingController): else: self.indicator_color = "green" self.indicator_title = _("Paid") - + + def on_recurring(self, reference_doc): + mcount = month_map[reference_doc.recurring_type] + self.set("delivery_date", get_next_date(reference_doc.delivery_date, mcount, + cint(reference_doc.repeat_on_day_of_month))) + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) @@ -328,17 +334,6 @@ def stop_or_unstop_sales_orders(names, status): frappe.local.message_log = [] - def before_recurring(self): - super(SalesOrder, self).before_recurring() - - for field in ("delivery_status", "per_delivered", "billing_status", "per_billed"): - self.set(field, None) - - for d in self.get("items"): - for field in ("delivered_qty", "billed_amt", "planned_qty", "prevdoc_docname"): - d.set(field, None) - - @frappe.whitelist() def make_material_request(source_name, target_doc=None): def postprocess(source, doc):