From e635a45be904c8cc62851e4825cbe28b4fbc47d8 Mon Sep 17 00:00:00 2001 From: mayur-patel Date: Thu, 21 Mar 2013 21:30:34 +1100 Subject: [PATCH 1/9] Added new default value with help of Anand Doshi. --- hr/doctype/employee/employee.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hr/doctype/employee/employee.py b/hr/doctype/employee/employee.py index 2bd7998159..16ecb6d2ed 100644 --- a/hr/doctype/employee/employee.py +++ b/hr/doctype/employee/employee.py @@ -17,7 +17,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import getdate, validate_email_add +from webnotes.utils import getdate, validate_email_add, cstr from webnotes.model.doc import make_autoname from webnotes import msgprint, _ @@ -71,7 +71,9 @@ class DocType: webnotes.conn.set_default("employee", self.doc.name, self.doc.user_id) webnotes.conn.set_default("employee_name", self.doc.employee_name, self.doc.user_id) webnotes.conn.set_default("company", self.doc.company, self.doc.user_id) - + if self.doc.reports_to: + webnotes.conn.set_default("leave_approver", webnotes.conn.get_value("Employee", self.doc.reports_to, "user_id"), self.doc.user_id) + def update_profile(self): # add employee role if missing if not "Employee" in webnotes.conn.sql_list("""select role from tabUserRole @@ -104,7 +106,7 @@ class DocType: fname, fid = file_args.split(",") if self.doc.image == fname: new_file_args = fname + "," + fid - file_list = profile_wrapper.doc.file_list.split("\n") + file_list = cstr(profile_wrapper.doc.file_list).split("\n") if new_file_args not in file_list: file_list += [new_file_args] profile_wrapper.doc.file_list = "\n".join(file_list) From 162a081a9f70bdc6eb7894591f75bb143b8663a7 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 21 Mar 2013 17:30:22 +0530 Subject: [PATCH 2/9] unchecked Not In Create in item customer detail and item price --- stock/doctype/item_customer_detail/item_customer_detail.txt | 6 +++--- stock/doctype/item_price/item_price.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/stock/doctype/item_customer_detail/item_customer_detail.txt b/stock/doctype/item_customer_detail/item_customer_detail.txt index 2ee945048b..3b916a22d4 100644 --- a/stock/doctype/item_customer_detail/item_customer_detail.txt +++ b/stock/doctype/item_customer_detail/item_customer_detail.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:28:01", + "creation": "2013-03-08 15:37:16", "docstatus": 0, - "modified": "2013-03-07 07:03:22", + "modified": "2013-03-21 17:29:45", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,7 +10,7 @@ "autoname": "ITEMCUST/.#####", "description": "For the convenience of customers, these codes can be used in print formats like Invoices and Delivery Notes", "doctype": "DocType", - "in_create": 1, + "in_create": 0, "istable": 1, "module": "Stock", "name": "__common__", diff --git a/stock/doctype/item_price/item_price.txt b/stock/doctype/item_price/item_price.txt index 863ca25389..721902bc72 100644 --- a/stock/doctype/item_price/item_price.txt +++ b/stock/doctype/item_price/item_price.txt @@ -1,15 +1,15 @@ [ { - "creation": "2013-02-22 01:28:01", + "creation": "2013-03-08 15:37:16", "docstatus": 0, - "modified": "2013-03-07 07:03:22", + "modified": "2013-03-21 17:29:15", "modified_by": "Administrator", "owner": "Administrator" }, { "autoname": "RFD/.#####", "doctype": "DocType", - "in_create": 1, + "in_create": 0, "istable": 1, "module": "Stock", "name": "__common__", From a0e7c1539b34556de94e4d5500d421f11460d855 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 21 Mar 2013 18:37:06 +0530 Subject: [PATCH 3/9] aii: book expense in delivery note instead of sales invoice --- .../doctype/sales_invoice/sales_invoice.py | 80 +++---------------- .../sales_invoice/test_sales_invoice.py | 64 --------------- .../sales_invoice_item/sales_invoice_item.txt | 5 +- controllers/selling_controller.py | 16 ++++ controllers/stock_controller.py | 16 +++- stock/doctype/delivery_note/delivery_note.js | 20 +++++ stock/doctype/delivery_note/delivery_note.py | 54 +++++-------- .../delivery_note/test_delivery_note.py | 9 ++- .../delivery_note_item/delivery_note_item.txt | 25 +++++- .../purchase_receipt/purchase_receipt.py | 6 +- stock/doctype/stock_entry/stock_entry.py | 12 ++- stock/doctype/stock_entry/test_stock_entry.py | 6 +- .../stock_reconciliation.py | 8 +- 13 files changed, 131 insertions(+), 190 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 22ac8458a8..9b5c80cfbb 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -27,8 +27,6 @@ from webnotes.model.bean import getlist from webnotes.model.code import get_obj from webnotes import _, msgprint -from stock.utils import get_buying_amount, get_sales_bom - session = webnotes.session month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12} @@ -701,19 +699,9 @@ class DocType(SellingController): }) ) - def make_item_gl_entries(self, gl_entries): - # item gl entries - auto_inventory_accounting = \ - cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) - - if auto_inventory_accounting: - if cint(self.doc.is_pos) and cint(self.doc.update_stock): - stock_account = self.get_default_account("stock_in_hand_account") - else: - stock_account = self.get_default_account("stock_delivered_but_not_billed") - + def make_item_gl_entries(self, gl_entries): + # income account gl entries for item in self.doclist.get({"parentfield": "entries"}): - # income account gl entries if flt(item.amount): gl_entries.append( self.get_gl_dict({ @@ -725,28 +713,16 @@ class DocType(SellingController): }) ) - # expense account gl entries - if auto_inventory_accounting and flt(item.buying_amount): + # expense account gl entries + if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \ + and cint(self.doc.is_pos) and cint(self.doc.update_stock): + + for item in self.doclist.get({"parentfield": "entries"}): self.check_expense_account(item) + + gl_entries += self.get_gl_entries_for_stock(item.expense_account, + -1*item.buying_amount, cost_center=item.cost_center) - gl_entries.append( - self.get_gl_dict({ - "account": item.expense_account, - "against": stock_account, - "debit": item.buying_amount, - "remarks": self.doc.remarks or "Accounting Entry for Stock", - "cost_center": item.cost_center - }) - ) - gl_entries.append( - self.get_gl_dict({ - "account": stock_account, - "against": item.expense_account, - "credit": item.buying_amount, - "remarks": self.doc.remarks or "Accounting Entry for Stock" - }) - ) - def make_pos_gl_entries(self, gl_entries): if cint(self.doc.is_pos) and self.doc.cash_bank_account and self.doc.paid_amount: # POS, make payment entries @@ -789,42 +765,6 @@ class DocType(SellingController): "cost_center": self.doc.write_off_cost_center }) ) - - def set_buying_amount(self): - if cint(self.doc.is_pos) and cint(self.doc.update_stock): - stock_ledger_entries = self.get_stock_ledger_entries() - item_sales_bom = get_sales_bom() - else: - stock_ledger_entries = item_sales_bom = None - - for item in self.doclist.get({"parentfield": "entries"}): - if item.item_code in self.stock_items or \ - (item_sales_bom and item_sales_bom.get(item.item_code)): - item.buying_amount = self.get_item_buying_amount(item, stock_ledger_entries, - item_sales_bom) - webnotes.conn.set_value("Sales Invoice Item", item.name, - "buying_amount", item.buying_amount) - - def get_item_buying_amount(self, item, stock_ledger_entries, item_sales_bom): - item_buying_amount = 0 - if stock_ledger_entries: - # is pos and update stock - item_buying_amount = get_buying_amount(item.item_code, item.warehouse, -1*item.qty, - self.doc.doctype, self.doc.name, item.name, stock_ledger_entries, item_sales_bom) - item.buying_amount = item_buying_amount > 0 and item_buying_amount or 0 - elif item.delivery_note and item.dn_detail: - # against delivery note - dn_item = webnotes.conn.get_value("Delivery Note Item", item.dn_detail, - ["buying_amount", "qty"], as_dict=1) - item_buying_rate = flt(dn_item.buying_amount) / flt(dn_item.qty) - item_buying_amount = item_buying_rate * flt(item.qty) - - return item_buying_amount - - def check_expense_account(self, item): - if not item.expense_account: - msgprint(_("""Expense account is mandatory for item: """) + item.item_code, - raise_exception=1) def update_c_form(self): """Update amended id in C-form""" diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index 92feae8153..7aa0c27d06 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -86,68 +86,6 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(gle_count[0][0], 8) - def test_sales_invoice_gl_entry_with_aii_delivery_note(self): - webnotes.conn.sql("delete from `tabStock Ledger Entry`") - - webnotes.defaults.set_global_default("auto_inventory_accounting", 1) - - self._insert_purchase_receipt() - dn = self._insert_delivery_note() - - si_against_dn = webnotes.copy_doclist(test_records[1]) - si_against_dn[1]["delivery_note"] = dn.doc.name - si_against_dn[1]["dn_detail"] = dn.doclist[1].name - si = webnotes.bean(si_against_dn) - si.insert() - - si.submit() - - gl_entries = webnotes.conn.sql("""select account, debit, credit - from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s - order by account asc""", si.doc.name, as_dict=1) - self.assertTrue(gl_entries) - - expected_values = sorted([ - [si.doc.debit_to, 630.0, 0.0], - [test_records[1][1]["income_account"], 0.0, 500.0], - [test_records[1][2]["account_head"], 0.0, 80.0], - [test_records[1][3]["account_head"], 0.0, 50.0], - ["Stock Delivered But Not Billed - _TC", 0.0, 375.0], - [test_records[1][1]["expense_account"], 375.0, 0.0] - ]) - for i, gle in enumerate(gl_entries): - self.assertEquals(expected_values[i][0], gle.account) - self.assertEquals(expected_values[i][1], gle.debit) - self.assertEquals(expected_values[i][2], gle.credit) - - si.cancel() - gl_entries = webnotes.conn.sql("""select account, debit, credit - from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s - and ifnull(is_cancelled, 'No') = 'No' - order by account asc, name asc""", si.doc.name, as_dict=1) - - expected_values = sorted([ - [si.doc.debit_to, 630.0, 0.0], - [si.doc.debit_to, 0.0, 630.0], - [test_records[1][1]["income_account"], 0.0, 500.0], - [test_records[1][1]["income_account"], 500.0, 0.0], - [test_records[1][2]["account_head"], 0.0, 80.0], - [test_records[1][2]["account_head"], 80.0, 0.0], - [test_records[1][3]["account_head"], 0.0, 50.0], - [test_records[1][3]["account_head"], 50.0, 0.0], - ["Stock Delivered But Not Billed - _TC", 0.0, 375.0], - ["Stock Delivered But Not Billed - _TC", 375.0, 0.0], - [test_records[1][1]["expense_account"], 375.0, 0.0], - [test_records[1][1]["expense_account"], 0.0, 375.0] - - ]) - for i, gle in enumerate(gl_entries): - self.assertEquals(expected_values[i][0], gle.account) - self.assertEquals(expected_values[i][1], gle.debit) - self.assertEquals(expected_values[i][2], gle.credit) - - webnotes.defaults.set_global_default("auto_inventory_accounting", 0) - def test_pos_gl_entry_with_aii(self): webnotes.conn.sql("delete from `tabStock Ledger Entry`") webnotes.defaults.set_global_default("auto_inventory_accounting", 1) @@ -262,8 +200,6 @@ class TestSalesInvoice(unittest.TestCase): webnotes.defaults.set_global_default("auto_inventory_accounting", 0) - - def _insert_purchase_receipt(self): from stock.doctype.purchase_receipt.test_purchase_receipt import test_records \ as pr_test_records diff --git a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt index 6f6ad399c5..bc51198f3b 100644 --- a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt +++ b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 11:42:55", "docstatus": 0, - "modified": "2013-03-18 15:41:19", + "modified": "2013-03-21 18:35:47", "modified_by": "Administrator", "owner": "Administrator" }, @@ -207,11 +207,10 @@ "width": "120px" }, { - "depends_on": "eval:sys_defaults.auto_inventory_accounting", "doctype": "DocField", "fieldname": "expense_account", "fieldtype": "Link", - "hidden": 0, + "hidden": 1, "in_filter": 1, "label": "Expense Account", "options": "Account", diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index cd81e18d60..fa8927c81a 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -39,3 +39,19 @@ class SellingController(StockController): if self.meta.get_field("in_words_export"): self.doc.in_words_export = money_in_words(disable_rounded_total and self.doc.grand_total_export or self.doc.rounded_total_export, self.doc.currency) + + def set_buying_amount(self): + from stock.utils import get_buying_amount, get_sales_bom + stock_ledger_entries = self.get_stock_ledger_entries() + item_sales_bom = get_sales_bom() + + if stock_ledger_entries: + for item in self.doclist.get({"parentfield": self.fname}): + if item.item_code in self.stock_items or \ + (item_sales_bom and item_sales_bom.get(item.item_code)): + buying_amount = get_buying_amount(item.item_code, item.warehouse, -1*item.qty, + self.doc.doctype, self.doc.name, item.name, stock_ledger_entries, + item_sales_bom) + item.buying_amount = buying_amount > 0 and buying_amount or 0 + webnotes.conn.set_value(self.tname, item.name, "buying_amount", + item.buying_amount) \ No newline at end of file diff --git a/controllers/stock_controller.py b/controllers/stock_controller.py index eec7352f1a..c76865d371 100644 --- a/controllers/stock_controller.py +++ b/controllers/stock_controller.py @@ -16,11 +16,14 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ from controllers.accounts_controller import AccountsController class StockController(AccountsController): - def make_gl_entries(self, against_stock_account, amount, cost_center=None): - stock_in_hand_account = self.get_default_account("stock_in_hand_account") + def get_gl_entries_for_stock(self, against_stock_account, amount, + stock_in_hand_account=None, cost_center=None): + if not stock_in_hand_account: + stock_in_hand_account = self.get_default_account("stock_in_hand_account") if amount: gl_entries = [ @@ -41,9 +44,14 @@ class StockController(AccountsController): "remarks": self.doc.remarks or "Accounting Entry for Stock", }, self.doc.docstatus == 2), ] - from accounts.general_ledger import make_gl_entries - make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2) + return gl_entries + + + def check_expense_account(self, item): + if not item.expense_account: + msgprint(_("""Expense account is mandatory for item: """) + item.item_code, + raise_exception=1) def get_stock_ledger_entries(self, item_list=None, warehouse_list=None): if not (item_list and warehouse_list): diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index a8af1073fe..7e6031c6f9 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -308,4 +308,24 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) { if(cint(wn.boot.notification_settings.delivery_note)) { cur_frm.email_doc(wn.boot.notification_settings.delivery_note_message); } +} + +// expense account +cur_frm.fields_dict['delivery_note_details'].grid.get_field('expense_account').get_query = function(doc) { + return { + "query": "accounts.utils.get_account_list", + "filters": { + "is_pl_account": "Yes", + "debit_or_credit": "Debit", + "company": doc.company + } + } +} + +// cost center +cur_frm.fields_dict["delivery_note_details"].grid.get_field("cost_center").get_query = function(doc) { + return { + query: "accounts.utils.get_cost_center_list", + filters: { company_name: doc.company} + } } \ No newline at end of file diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index cb7263be7c..6bc661ddd8 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -144,6 +144,8 @@ class DocType(SellingController): self.validate_mandatory() self.validate_reference_value() self.validate_for_items() + self.validate_warehouse() + sales_com_obj.validate_max_discount(self, 'delivery_note_details') sales_com_obj.get_allocated_sum(self) sales_com_obj.check_conversion_rate(self) @@ -203,6 +205,12 @@ class DocType(SellingController): else: chk_dupl_itm.append(f) + def validate_warehouse(self): + for d in self.get_item_list(): + if webnotes.conn.get_value("Item", d['item_code'], "is_stock_item") == "Yes": + if not d['warehouse']: + msgprint("Please enter Warehouse for item %s as it is stock item" + % d['item_code'], raise_exception=1) def validate_items_with_prevdoc(self, d): """check if same item, warehouse present in prevdoc""" @@ -393,41 +401,17 @@ class DocType(SellingController): total = (amount/self.doc.net_total)*self.doc.grand_total get_obj('Sales Common').check_credit(self, total) - def set_buying_amount(self): - from stock.utils import get_buying_amount, get_sales_bom - stock_ledger_entries = self.get_stock_ledger_entries() - item_sales_bom = get_sales_bom() - - if stock_ledger_entries: - for item in self.doclist.get({"parentfield": "delivery_note_details"}): - if item.item_code in self.stock_items or \ - (item_sales_bom and item_sales_bom.get(item.item_code)): - buying_amount = get_buying_amount(item.item_code, item.warehouse, -1*item.qty, - self.doc.doctype, self.doc.name, item.name, stock_ledger_entries, - item_sales_bom) - item.buying_amount = buying_amount > 0 and buying_amount or 0 - webnotes.conn.set_value("Delivery Note Item", item.name, "buying_amount", - item.buying_amount) - - self.validate_warehouse() - - def validate_warehouse(self): - for d in self.get_item_list(): - if webnotes.conn.get_value("Item", d['item_code'], "is_stock_item") == "Yes": - if not d['warehouse']: - msgprint("Please enter Warehouse for item %s as it is stock item" - % d['item_code'], raise_exception=1) - def make_gl_entries(self): if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")): return - - against_stock_account = self.get_default_account("stock_delivered_but_not_billed") - total_buying_amount = self.get_total_buying_amount() - - super(DocType, self).make_gl_entries(against_stock_account, -1*total_buying_amount) - - def get_total_buying_amount(self): - total_buying_amount = sum([item.buying_amount for item in - self.doclist.get({"parentfield": "delivery_note_details"})]) - return total_buying_amount + + gl_entries = [] + for item in self.doclist.get({"parentfield": "delivery_note_details"}): + self.check_expense_account(item) + + gl_entries += self.get_gl_entries_for_stock(item.expense_account, -1*item.buying_amount, + cost_center=item.cost_center) + + if gl_entries: + from accounts.general_ledger import make_gl_entries + make_gl_entries(gl_entries) \ No newline at end of file diff --git a/stock/doctype/delivery_note/test_delivery_note.py b/stock/doctype/delivery_note/test_delivery_note.py index d0b440cfee..d0ec631db0 100644 --- a/stock/doctype/delivery_note/test_delivery_note.py +++ b/stock/doctype/delivery_note/test_delivery_note.py @@ -56,15 +56,19 @@ class TestDeliveryNote(unittest.TestCase): self._insert_purchase_receipt() dn = webnotes.bean(copy=test_records[0]) + dn.doclist[1].expense_account = "Cost of Goods Sold - _TC" + dn.doclist[1].cost_center = "Auto Inventory Accounting - _TC" + stock_in_hand_account = webnotes.conn.get_value("Company", dn.doc.company, "stock_in_hand_account") from accounts.utils import get_balance_on prev_bal = get_balance_on(stock_in_hand_account, dn.doc.posting_date) - + dn.insert() dn.submit() + gl_entries = webnotes.conn.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Delivery Note' and voucher_no=%s order by account asc""", dn.doc.name, as_dict=1) @@ -72,9 +76,8 @@ class TestDeliveryNote(unittest.TestCase): expected_values = sorted([ [stock_in_hand_account, 0.0, 375.0], - ["Stock Delivered But Not Billed - _TC", 375.0, 0.0] + ["Cost of Goods Sold - _TC", 375.0, 0.0] ]) - for i, gle in enumerate(gl_entries): self.assertEquals(expected_values[i][0], gle.account) self.assertEquals(expected_values[i][1], gle.debit) diff --git a/stock/doctype/delivery_note_item/delivery_note_item.txt b/stock/doctype/delivery_note_item/delivery_note_item.txt index 47b0d33bcd..94c6541172 100644 --- a/stock/doctype/delivery_note_item/delivery_note_item.txt +++ b/stock/doctype/delivery_note_item/delivery_note_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 11:42:59", "docstatus": 0, - "modified": "2013-03-07 15:46:43", + "modified": "2013-03-21 18:36:22", "modified_by": "Administrator", "owner": "Administrator" }, @@ -236,6 +236,7 @@ "doctype": "DocField", "fieldname": "batch_no", "fieldtype": "Link", + "hidden": 0, "label": "Batch No", "oldfieldname": "batch_no", "oldfieldtype": "Link", @@ -243,6 +244,28 @@ "print_hide": 1, "read_only": 0 }, + { + "doctype": "DocField", + "fieldname": "expense_account", + "fieldtype": "Link", + "hidden": 1, + "label": "Expense Account", + "no_copy": 1, + "options": "Account", + "print_hide": 1, + "width": "120px" + }, + { + "doctype": "DocField", + "fieldname": "cost_center", + "fieldtype": "Link", + "hidden": 1, + "label": "Cost Center", + "no_copy": 1, + "options": "Cost Center", + "print_hide": 1, + "width": "120px" + }, { "doctype": "DocField", "fieldname": "item_group", diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py index e7d030d719..7949a1c1c3 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/stock/doctype/purchase_receipt/purchase_receipt.py @@ -318,10 +318,14 @@ class DocType(BuyingController): if not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")): return + from accounts.general_ledger import make_gl_entries + against_stock_account = self.get_default_account("stock_received_but_not_billed") total_valuation_amount = self.get_total_valuation_amount() + gl_entries = self.get_gl_entries_for_stock(against_stock_account, total_valuation_amount) - super(DocType, self).make_gl_entries(against_stock_account, total_valuation_amount) + if gl_entries: + make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2) def get_total_valuation_amount(self): total_valuation_amount = 0.0 diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 76d3a423b1..f54ce605a0 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -173,13 +173,17 @@ class DocType(StockController): if not self.doc.expense_adjustment_account: webnotes.msgprint(_("Please enter Expense/Adjustment Account"), raise_exception=1) - + + from accounts.general_ledger import make_gl_entries + cost_center = "Auto Inventory Accounting - %s" % (self.company_abbr,) total_valuation_amount = self.get_total_valuation_amount() - super(DocType, self).make_gl_entries(self.doc.expense_adjustment_account, - total_valuation_amount, cost_center) - + gl_entries = self.get_gl_entries_for_stock(self.doc.expense_adjustment_account, + total_valuation_amount, cost_center=cost_center) + if gl_entries: + make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2) + def get_total_valuation_amount(self): total_valuation_amount = 0 for item in self.doclist.get({"parentfield": "mtn_details"}): diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index 1bd068aa62..ded71db26b 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -26,7 +26,7 @@ class TestStockEntry(unittest.TestCase): self.assertTrue(mr_name) - def atest_material_receipt_gl_entry(self): + def test_material_receipt_gl_entry(self): webnotes.conn.sql("delete from `tabStock Ledger Entry`") webnotes.defaults.set_global_default("auto_inventory_accounting", 1) @@ -63,7 +63,7 @@ class TestStockEntry(unittest.TestCase): webnotes.defaults.set_global_default("auto_inventory_accounting", 0) - def atest_material_issue_gl_entry(self): + def test_material_issue_gl_entry(self): webnotes.conn.sql("delete from `tabStock Ledger Entry`") webnotes.defaults.set_global_default("auto_inventory_accounting", 1) @@ -151,7 +151,7 @@ class TestStockEntry(unittest.TestCase): self.assertEquals(expected_sle[i][1], sle.warehouse) self.assertEquals(expected_sle[i][2], sle.actual_qty) - def acheck_gl_entries(self, voucher_type, voucher_no, expected_gl_entries): + def check_gl_entries(self, voucher_type, voucher_no, expected_gl_entries): # check gl entries gl_entries = webnotes.conn.sql("""select account, debit, credit diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.py b/stock/doctype/stock_reconciliation/stock_reconciliation.py index c2f5a940c5..ac0ab987f4 100644 --- a/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -309,10 +309,14 @@ class DocType(StockController): if not self.doc.expense_account: msgprint(_("Please enter Expense Account"), raise_exception=1) + from accounts.general_ledger import make_gl_entries + cost_center = "Auto Inventory Accounting - %s" % (self.company_abbr,) - super(DocType, self).make_gl_entries(self.doc.expense_account, - self.doc.stock_value_difference, cost_center) + gl_entries = self.get_gl_entries_for_stock(self.doc.expense_account, + self.doc.stock_value_difference, cost_center=cost_center) + if gl_entries: + make_gl_entries(gl_entries, cancel=self.doc.docstatus == 2) @webnotes.whitelist() def upload(): From 6d8d3b4ceeca8b74ebe790557567e58ec71dbdd9 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Thu, 21 Mar 2013 18:45:02 +0530 Subject: [PATCH 4/9] fixes in gross profit report --- .../doctype/sales_invoice/sales_invoice.py | 2 +- accounts/report/gross_profit/gross_profit.py | 7 +++- stock/doctype/delivery_note/delivery_note.py | 2 +- stock/utils.py | 41 +++++++++++++------ 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 22ac8458a8..620859e567 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -793,7 +793,7 @@ class DocType(SellingController): def set_buying_amount(self): if cint(self.doc.is_pos) and cint(self.doc.update_stock): stock_ledger_entries = self.get_stock_ledger_entries() - item_sales_bom = get_sales_bom() + item_sales_bom = get_sales_bom(self.doc.doctype, self.doc.name) else: stock_ledger_entries = item_sales_bom = None diff --git a/accounts/report/gross_profit/gross_profit.py b/accounts/report/gross_profit/gross_profit.py index 2480e17679..a871bab73e 100644 --- a/accounts/report/gross_profit/gross_profit.py +++ b/accounts/report/gross_profit/gross_profit.py @@ -7,7 +7,8 @@ def execute(filters=None): if not filters: filters = {} stock_ledger_entries = get_stock_ledger_entries(filters) - item_sales_bom = get_sales_bom() + + item_sales_bom = get_sales_bom("Delivery Note") delivery_note_items = webnotes.conn.sql("""select dn.name, dn.posting_date, dn.posting_time, dn.project_name, item.item_code, item.item_name, item.description, item.warehouse, @@ -25,7 +26,9 @@ def execute(filters=None): for row in delivery_note_items: selling_amount = flt(row.amount) buying_amount = get_buying_amount(row.item_code, row.warehouse, -1*row.qty, - "Delivery Note", row.name, row.item_row, stock_ledger_entries, item_sales_bom) + "Delivery Note", row.name, row.item_row, stock_ledger_entries, + item_sales_bom.get(row.name, webnotes._dict())) + buying_amount = buying_amount > 0 and buying_amount or 0 if selling_amount: diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index cb7263be7c..82881c5af5 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -396,7 +396,7 @@ class DocType(SellingController): def set_buying_amount(self): from stock.utils import get_buying_amount, get_sales_bom stock_ledger_entries = self.get_stock_ledger_entries() - item_sales_bom = get_sales_bom() + item_sales_bom = get_sales_bom(self.doc.doctype, self.doc.name) if stock_ledger_entries: for item in self.doclist.get({"parentfield": "delivery_note_details"}): diff --git a/stock/utils.py b/stock/utils.py index b4d07701ff..047ff33a7d 100644 --- a/stock/utils.py +++ b/stock/utils.py @@ -171,7 +171,8 @@ def get_buying_amount(item_code, warehouse, qty, voucher_type, voucher_no, vouch buying_amount = 0.0 for bom_item in item_sales_bom[item_code]: buying_amount += _get_buying_amount(voucher_type, voucher_no, "[** No Item Row **]", - bom_item.item_code, warehouse, bom_item.qty * qty, stock_ledger_entries) + bom_item.item_code, bom_item.warehouse or warehouse, + bom_item.total_qty or (bom_item.qty * qty), stock_ledger_entries) return buying_amount else: # doesn't have sales bom @@ -184,21 +185,37 @@ def _get_buying_amount(voucher_type, voucher_no, item_row, item_code, warehouse, if sle.voucher_type == voucher_type and sle.voucher_no == voucher_no and \ (sle.voucher_detail_no == item_row or (sle.voucher_type != "Stock Reconciliation" and sle.item_code == item_code and sle.warehouse == warehouse and flt(sle.qty) == qty)): - # print "previous_sle", stock_ledger_entries[i+1] - # print "current sle", sle previous_stock_value = len(stock_ledger_entries) > i+1 and \ flt(stock_ledger_entries[i+1].stock_value) or 0.0 buying_amount = previous_stock_value - flt(sle.stock_value) + return buying_amount return 0.0 -def get_sales_bom(): - item_sales_bom = {} - # for r in webnotes.conn.sql("""select parent_item, item_code, qty, warehouse, voucher_detail_no - # from `tabDelivery Note Packing Item` where docstatus = 1""", as_dict=1): - for r in webnotes.conn.sql("""select parent, item_code, qty from `tabSales BOM Item`""", - as_dict=1): - item_sales_bom.setdefault(r.parent, []).append(r) - - return item_sales_bom \ No newline at end of file +def get_sales_bom(doctype=None, docname=None): + item_sales_bom = webnotes._dict() + + query = """select parenttype, parent, parent_item, + item_code, warehouse, -1*qty as total_qty + from `tabDelivery Note Packing Item` where docstatus=1""" + + args = {} + if doctype: + query += " and parenttype=%(parenttype)s" + args["parenttype"] = doctype + + if docname: + query += " and parent=%(parent)s" + args["parent"] = docname + + for d in webnotes.conn.sql(query, args, as_dict=1): + item_sales_bom.setdefault(d.parenttype, webnotes._dict()).setdefault(d.parent, + webnotes._dict()).setdefault(d.parent_item, []).append(d) + + if doctype and docname: + return item_sales_bom[doctype].get(docname, webnotes._dict()) + elif doctype: + return item_sales_bom[doctype] + else: + return item_sales_bom From 9d3f0405b7de4c63e7dcc426d188a4813db0ff94 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 Mar 2013 12:02:48 +0530 Subject: [PATCH 5/9] fixes in packing slip --- stock/doctype/packing_slip/packing_slip.js | 6 ++- stock/doctype/packing_slip/packing_slip.py | 46 +++++++++------------- 2 files changed, 23 insertions(+), 29 deletions(-) diff --git a/stock/doctype/packing_slip/packing_slip.js b/stock/doctype/packing_slip/packing_slip.js index 706c6f5141..e9396d9edc 100644 --- a/stock/doctype/packing_slip/packing_slip.js +++ b/stock/doctype/packing_slip/packing_slip.js @@ -19,7 +19,8 @@ cur_frm.fields_dict['delivery_note'].get_query = function(doc, cdt, cdn) { } -cur_frm.fields_dict['item_details'].grid.get_field('item_code').get_query = function(doc, cdt, cdn) { +cur_frm.fields_dict['item_details'].grid.get_field('item_code').get_query = + function(doc, cdt, cdn) { var query = 'SELECT name, item_name, description FROM `tabItem` WHERE name IN ( \ SELECT item_code FROM `tabDelivery Note Item` dnd \ WHERE parent="' + doc.delivery_note + '" AND IFNULL(qty, 0) > IFNULL(packed_qty, 0)) AND %(key)s LIKE "%s" LIMIT 50'; @@ -34,9 +35,10 @@ cur_frm.add_fetch("item_code", "net_weight", "net_weight"); cur_frm.add_fetch("item_code", "weight_uom", "weight_uom"); cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { + console.log(make_doclist(cdt, cdn)); if(doc.delivery_note && doc.__islocal) { var ps_detail = getchildren('Packing Slip Item', doc.name, 'item_details'); - if(!(flt(ps_detail[0].net_weight) && cstr(ps_detail[0].weight_uom))) { + if(!(flt(ps_detail.net_weight) && cstr(ps_detail.weight_uom))) { cur_frm.cscript.update_item_details(doc); } } diff --git a/stock/doctype/packing_slip/packing_slip.py b/stock/doctype/packing_slip/packing_slip.py index 5f4fda05a1..72682b2de3 100644 --- a/stock/doctype/packing_slip/packing_slip.py +++ b/stock/doctype/packing_slip/packing_slip.py @@ -40,15 +40,12 @@ class DocType: """ Validates if delivery note has status as submitted """ - res = webnotes.conn.sql("""\ - SELECT docstatus FROM `tabDelivery Note` - WHERE name=%(delivery_note)s - """, self.doc.fields) + res = webnotes.conn.sql("""SELECT docstatus FROM `tabDelivery Note` + WHERE name=%(delivery_note)s""", self.doc.fields) if not(res and res[0][0]==0): webnotes.msgprint("""Invalid Delivery Note. Delivery Note should exist - and should be in draft state. Please rectify and try again.""", - raise_exception=1) + and should be in draft state. Please rectify and try again.""", raise_exception=1) def validate_case_nos(self): @@ -65,11 +62,10 @@ class DocType: raise_exception=1) - res = webnotes.conn.sql("""\ - SELECT name FROM `tabPacking Slip` + res = webnotes.conn.sql("""SELECT name FROM `tabPacking Slip` WHERE delivery_note = %(delivery_note)s AND docstatus = 1 AND (from_case_no BETWEEN %(from_case_no)s AND %(to_case_no)s - OR to_case_no BETWEEN %(from_case_no)s AND %(to_case_no)s)\ + OR to_case_no BETWEEN %(from_case_no)s AND %(to_case_no)s) """, self.doc.fields) if res: @@ -133,10 +129,10 @@ class DocType: item['recommended_qty'] = (flt(item['qty']) - flt(item['packed_qty'])) / no_of_cases item['specified_qty'] = flt(ps_item_qty[item['item_code']]) - webnotes.msgprint("""\ + webnotes.msgprint(""" Invalid Quantity specified (%(specified_qty)s %(stock_uom)s). %(packed_qty)s out of %(qty)s %(stock_uom)s already packed for %(item_code)s. - Recommended quantity for %(item_code)s = %(recommended_qty)s \ + Recommended quantity for %(item_code)s = %(recommended_qty)s %(stock_uom)s""" % item, raise_exception=1) @@ -167,16 +163,15 @@ class DocType: for item in dn_details: new_packed_qty = flt(item['packed_qty']) if (new_packed_qty < 0) or (new_packed_qty > flt(item['qty'])): - webnotes.msgprint("Invalid new packed quantity for item %s. \ - Please try again or contact support@erpnext.com" % item['item_code'], raise_exception=1) + webnotes.msgprint("""Invalid new packed quantity for item %s. + Please try again or contact support@erpnext.com""" % + item['item_code'], raise_exception=1) - webnotes.conn.sql("""\ - UPDATE `tabDelivery Note Item` - SET packed_qty = %s - WHERE parent = %s AND item_code = %s""", + webnotes.conn.sql("""UPDATE `tabDelivery Note Item` + SET packed_qty = %s WHERE parent = %s AND item_code = %s""", (new_packed_qty, self.doc.delivery_note, item['item_code'])) - webnotes.conn.set_value("Delivery Note", self.doc.delivery_note, - "modified", now()) + + webnotes.conn.set_value("Delivery Note", self.doc.delivery_note, "modified", now()) def update_item_details(self): @@ -191,8 +186,7 @@ class DocType: def set_item_details(self, row): - res = webnotes.conn.sql("""\ - SELECT item_name, SUM(IFNULL(qty, 0)) as total_qty, + res = webnotes.conn.sql("""SELECT item_name, SUM(IFNULL(qty, 0)) as total_qty, IFNULL(packed_qty, 0) as packed_qty, stock_uom FROM `tabDelivery Note Item` WHERE parent=%s AND item_code=%s GROUP BY item_code""", @@ -203,10 +197,9 @@ class DocType: if not row.qty: row.qty = qty >= 0 and qty or 0 - res = webnotes.conn.sql("""\ - SELECT net_weight, weight_uom FROM `tabItem` - WHERE name=%s""", self.doc.item_code, as_dict=1) - + res = webnotes.conn.sql("""SELECT net_weight, weight_uom FROM `tabItem` + WHERE name=%s""", row.item_code, as_dict=1) + if res and len(res)>0: row.net_weight = res[0]["net_weight"] row.weight_uom = res[0]["weight_uom"] @@ -217,8 +210,7 @@ class DocType: Returns the next case no. for a new packing slip for a delivery note """ - recommended_case_no = webnotes.conn.sql("""\ - SELECT MAX(to_case_no) FROM `tabPacking Slip` + recommended_case_no = webnotes.conn.sql("""SELECT MAX(to_case_no) FROM `tabPacking Slip` WHERE delivery_note = %(delivery_note)s AND docstatus=1""", self.doc.fields) return cint(recommended_case_no[0][0]) + 1 \ No newline at end of file From 54e79995ee3eec585475784d19cf2e17a27c93ec Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 22 Mar 2013 12:35:11 +0530 Subject: [PATCH 6/9] fixes in trend analyzer --- accounts/search_criteria/trend_analyzer/trend_analyzer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/accounts/search_criteria/trend_analyzer/trend_analyzer.js b/accounts/search_criteria/trend_analyzer/trend_analyzer.js index 7f4b94b34b..59d948342a 100644 --- a/accounts/search_criteria/trend_analyzer/trend_analyzer.js +++ b/accounts/search_criteria/trend_analyzer/trend_analyzer.js @@ -16,6 +16,7 @@ report.customize_filters = function() { this.hide_all_filters(); + this.dt.set_no_limit(1); // hide transaction based on permissions var all_transactions = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", From a3b9b1a8686765b860ef9795a662edfb9604a0a7 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 22 Mar 2013 13:16:10 +0530 Subject: [PATCH 7/9] added filters from date and to date --- accounts/report/gross_profit/gross_profit.js | 12 ++++++++++++ accounts/report/gross_profit/gross_profit.py | 13 +++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/accounts/report/gross_profit/gross_profit.js b/accounts/report/gross_profit/gross_profit.js index 1f07df9e81..aa6be5528f 100644 --- a/accounts/report/gross_profit/gross_profit.js +++ b/accounts/report/gross_profit/gross_profit.js @@ -7,5 +7,17 @@ wn.query_reports["Gross Profit"] = { "options": "Company", "default": wn.defaults.get_user_default("company") }, + { + "fieldname":"from_date", + "label": "From Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_start_date") + }, + { + "fieldname":"to_date", + "label": "To Date", + "fieldtype": "Date", + "default": wn.defaults.get_user_default("year_end_date") + }, ] } \ No newline at end of file diff --git a/accounts/report/gross_profit/gross_profit.py b/accounts/report/gross_profit/gross_profit.py index a4791616a1..5c06637fda 100644 --- a/accounts/report/gross_profit/gross_profit.py +++ b/accounts/report/gross_profit/gross_profit.py @@ -8,10 +8,10 @@ def execute(filters=None): stock_ledger_entries = get_stock_ledger_entries(filters) - item_sales_bom = get_item_sales_bom() - source = get_source_data(filters) + item_sales_bom = get_item_sales_bom() + columns = ["Delivery Note/Sales Invoice::120", "Posting Date:Date", "Posting Time", "Item Code:Link/Item", "Item Name", "Description", "Warehouse:Link/Warehouse", "Qty:Float", "Selling Rate:Currency", "Selling Amount:Currency", "Buying Amount:Currency", @@ -43,7 +43,8 @@ def get_stock_ledger_entries(filters): query = """select item_code, voucher_type, voucher_no, voucher_detail_no, posting_date, posting_time, stock_value, warehouse, actual_qty as qty - from `tabStock Ledger Entry` where ifnull(`is_cancelled`, "No") = "No" """ + from `tabStock Ledger Entry` + where ifnull(`is_cancelled`, "No") = "No" """ if filters.get("company"): query += """ and company=%(company)s""" @@ -57,7 +58,7 @@ def get_item_sales_bom(): for d in webnotes.conn.sql("""select parenttype, parent, parent_item, item_code, warehouse, -1*qty as total_qty - from `tabDelivery Note Packing Item` where docstatus=1""", as_dict=1): + from `tabDelivery Note Packing Item` where docstatus=1""", as_dict=True): item_sales_bom.setdefault(d.parenttype, webnotes._dict()).setdefault(d.parent, webnotes._dict()).setdefault(d.parent_item, []).append(d) @@ -67,6 +68,10 @@ def get_source_data(filters): conditions = "" if filters.get("company"): conditions += " and company=%(company)s" + if filters.get("from_date"): + conditions += " and posting_date>=%(from_date)s" + if filters.get("to_date"): + conditions += " and posting_date<=%(to_date)s" delivery_note_items = webnotes.conn.sql("""select item.parenttype, dn.name, dn.posting_date, dn.posting_time, dn.project_name, From 1b08d88bb4ec91fbad0cb62563681280c88fbaac Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 22 Mar 2013 13:29:06 +0530 Subject: [PATCH 8/9] fixes in stock grid report - trim spaces in serial no --- public/js/stock_grid_report.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/js/stock_grid_report.js b/public/js/stock_grid_report.js index 6c43d823c3..173eb21845 100644 --- a/public/js/stock_grid_report.js +++ b/public/js/stock_grid_report.js @@ -64,7 +64,7 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({ // update balance (only needed in case of valuation) wh.balance_qty += sl.qty; wh.balance_value += value_diff; - + return value_diff; }, get_fifo_value_diff: function(wh, sl) { @@ -109,9 +109,9 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({ var value_diff = 0.0; - $.each(sl.serial_no.trim().split("\n"), function(i, serial_no) { - if(serial_no) { - value_diff += flt(me.serialized_buying_rates[serial_no]); + $.each(sl.serial_no.trim().split("\n"), function(i, sr) { + if(sr) { + value_diff += flt(me.serialized_buying_rates[sr.trim()]); } }); @@ -125,7 +125,7 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({ if(sle.qty > 0 && sle.serial_no) { $.each(sle.serial_no.trim().split("\n"), function(i, sr) { if(sr && sle.incoming_rate !== undefined) { - serialized_buying_rates[sr] = flt(sle.incoming_rate); + serialized_buying_rates[sr.trim()] = flt(sle.incoming_rate); } }); } From c8e64f5d42f401597876ccdedbd38e06615891a0 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Fri, 22 Mar 2013 16:49:16 +0530 Subject: [PATCH 9/9] shifted field category of Purchase Taxes and Charges to the left --- .../purchase_taxes_and_charges.txt | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt b/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt index 62ee960759..b593b8175b 100644 --- a/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt +++ b/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.txt @@ -1,10 +1,10 @@ [ { - "creation": "2013-02-22 01:27:40", + "creation": "2013-03-08 15:36:47", "docstatus": 0, - "modified": "2013-03-07 07:03:29", + "modified": "2013-03-22 16:45:28", "modified_by": "Administrator", - "owner": "wasim@webnotestech.com" + "owner": "Administrator" }, { "autoname": "PVTD.######", @@ -26,6 +26,17 @@ "doctype": "DocType", "name": "Purchase Taxes and Charges" }, + { + "default": "Valuation and Total", + "doctype": "DocField", + "fieldname": "category", + "fieldtype": "Select", + "label": "Consider Tax or Charge for", + "oldfieldname": "category", + "oldfieldtype": "Select", + "options": "Valuation and Total\nValuation\nTotal", + "reqd": 1 + }, { "doctype": "DocField", "fieldname": "charge_type", @@ -105,17 +116,6 @@ "oldfieldname": "row_id", "oldfieldtype": "Data" }, - { - "default": "Valuation and Total", - "doctype": "DocField", - "fieldname": "category", - "fieldtype": "Select", - "label": "Consider Tax or Charge for", - "oldfieldname": "category", - "oldfieldtype": "Select", - "options": "Valuation and Total\nValuation\nTotal", - "reqd": 1 - }, { "default": "Add", "doctype": "DocField",