diff --git a/accounts/Print Format/Sales Invoice Classic/Sales Invoice Classic.txt b/accounts/Print Format/Sales Invoice Classic/Sales Invoice Classic.txt index d72c847850a..5aefa812f8c 100644 --- a/accounts/Print Format/Sales Invoice Classic/Sales Invoice Classic.txt +++ b/accounts/Print Format/Sales Invoice Classic/Sales Invoice Classic.txt @@ -1,17 +1,18 @@ [ { - "creation": "2013-02-01 14:16:04", + "creation": "2013-04-19 13:30:27", "docstatus": 0, - "modified": "2013-02-26 11:11:20", + "modified": "2013-05-28 17:19:38", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Invoice", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Accounts", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/accounts/Print Format/Sales Invoice Modern/Sales Invoice Modern.txt b/accounts/Print Format/Sales Invoice Modern/Sales Invoice Modern.txt index 97c50fd6df7..2f6251c07b9 100644 --- a/accounts/Print Format/Sales Invoice Modern/Sales Invoice Modern.txt +++ b/accounts/Print Format/Sales Invoice Modern/Sales Invoice Modern.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-05-15 18:39:02", + "creation": "2013-04-19 13:30:27", "docstatus": 0, - "modified": "2013-02-26 11:10:58", + "modified": "2013-05-28 17:19:52", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Invoice", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Accounts", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/accounts/Print Format/Sales Invoice Spartan/Sales Invoice Spartan.txt b/accounts/Print Format/Sales Invoice Spartan/Sales Invoice Spartan.txt index 6be83d45d13..b00b3d85f52 100644 --- a/accounts/Print Format/Sales Invoice Spartan/Sales Invoice Spartan.txt +++ b/accounts/Print Format/Sales Invoice Spartan/Sales Invoice Spartan.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-05-15 18:39:02", + "creation": "2013-04-19 13:30:27", "docstatus": 0, - "modified": "2013-02-26 11:11:40", + "modified": "2013-05-28 17:19:22", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Invoice", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Accounts", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index 92f17487bc7..c6a787e50a9 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -17,86 +17,56 @@ cur_frm.cscript.tname = "Purchase Invoice Item"; cur_frm.cscript.fname = "entries"; cur_frm.cscript.other_fname = "purchase_tax_details"; + +wn.provide("erpnext.accounts"); wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); wn.require('app/buying/doctype/purchase_common/purchase_common.js'); -erpnext.buying.PurchaseInvoiceController = erpnext.buying.BuyingController.extend({ +erpnext.accounts.PurchaseInvoiceController = erpnext.buying.BuyingController.extend({ + onload: function() { + this._super(); + + if(!this.frm.doc.__islocal) { + // show credit_to in print format + if(!this.frm.doc.supplier && this.frm.doc.credit_to) { + this.frm.set_df_property("credit_to", "print_hide", 0); + } + } + }, + refresh: function(doc) { this._super(); // Show / Hide button if(doc.docstatus==1 && doc.outstanding_amount > 0) - cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher); + this.frm.add_custom_button('Make Payment Entry', this.make_bank_voucher); if(doc.docstatus==1) { - cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry); + this.frm.add_custom_button('View Ledger', this.view_ledger_entry); } - cur_frm.cscript.is_opening(doc); + this.is_opening(doc); }, - onload_post_render: function(doc, dt, dn) { - var me = this; - var callback1 = function(doc, dt, dn) { - var callback2 = function(doc, dt, dn) { - if(doc.__islocal && doc.supplier) cur_frm.cscript.supplier(doc, dt, dn); - } - me.update_item_details(doc, dt, dn, callback2); - } - - // TODO: improve this - if(this.frm.doc.__islocal) { - if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) { - this.price_list_name(callback1); - } else { - callback1(doc, dt, dn); - } - } + + credit_to: function() { + this.supplier(); + }, + + write_off_amount: function() { + this.calculate_outstanding_amount(); + this.frm.refresh_fields(); + }, + + allocated_amount: function() { + this.calculate_total_advance("Purchase Invoice", "advance_allocation_details"); + this.frm.refresh_fields(); } }); -var new_cscript = new erpnext.buying.PurchaseInvoiceController({frm: cur_frm}); - // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new_cscript); +$.extend(cur_frm.cscript, new erpnext.accounts.PurchaseInvoiceController({frm: cur_frm})); -cur_frm.cscript.onload = function(doc,dt,dn) { - if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()}); -} - -cur_frm.cscript.supplier = function(doc,dt,dn) { - var callback = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - get_server_fields('get_credit_to','','',doc, dt, dn, 0, callback2); - } - - var callback2 = function(r,rt){ - var doc = locals[cur_frm.doctype][cur_frm.docname]; - var el = getchildren('Purchase Invoice Item',doc.name,'entries'); - for(var i in el){ - if(el[i].item_code && (!el[i].expense_head || !el[i].cost_center)){ - args = { - item_code: el[i].item_code, - expense_head: el[i].expense_head, - cost_center: el[i].cost_center - }; - get_server_fields('get_default_values', JSON.stringify(args), 'entries', doc, el[i].doctype, el[i].name, 1); - } - } - cur_frm.cscript.calc_amount(doc, 1); - } - - if (doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }),'', doc, dt, dn, 1, function(doc, dt, dn) { - cur_frm.refresh(); - callback(doc, dt, dn); - }); - unhide_field(['supplier_address','contact_person']); - } - -} - cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.supplier) get_server_fields('get_supplier_address', JSON.stringify({supplier: doc.supplier, address: doc.supplier_address, contact: doc.contact_person}),'', doc, dt, dn, 1); } @@ -112,23 +82,6 @@ cur_frm.fields_dict.contact_person.on_new = function(dn) { } -cur_frm.cscript.credit_to = function(doc,dt,dn) { - - var callback = function(doc, dt, dn) { - var doc = locals[doc.doctype][doc.name]; - if(doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }), '', doc, dt, dn, 1, function() { - cur_frm.refresh(); - }); - unhide_field(['supplier_address','contact_person']); - } - cur_frm.refresh(); - } - - get_server_fields('get_cust', '', '', doc, dt, dn, 1, callback); -} - cur_frm.fields_dict['entries'].grid.onrowadd = function(doc, cdt, cdn){ cl = getchildren('Purchase Invoice Item', doc.name, cur_frm.cscript.fname, doc.doctype); @@ -152,17 +105,6 @@ cur_frm.cscript.is_opening = function(doc, dt, dn) { if (doc.is_opening == 'Yes') unhide_field('aging_date'); } -cur_frm.cscript.write_off_amount = function(doc) { - doc.total_amount_to_pay = flt(doc.grand_total) - flt(doc.write_off_amount); - doc.outstanding_amount = flt(doc.total_amount_to_pay) - flt(doc.total_advance); - refresh_many(['outstanding_amount', 'total_amount_to_pay']); -} - -cur_frm.cscript.recalculate = function(doc, cdt, cdn) { - cur_frm.cscript.calculate_tax(doc,cdt,cdn); - calc_total_advance(doc,cdt,cdn); -} - cur_frm.cscript.get_items = function(doc, dt, dn) { var callback = function(r,rt) { unhide_field(['supplier_address', 'contact_person']); @@ -171,11 +113,6 @@ cur_frm.cscript.get_items = function(doc, dt, dn) { $c_obj(make_doclist(dt,dn),'pull_details','',callback); } -cur_frm.cscript.allocated_amount = function(doc,cdt,cdn) { - calc_total_advance(doc, cdt, cdn); -} - - cur_frm.cscript.make_bank_voucher = function() { wn.call({ method: "accounts.doctype.journal_voucher.journal_voucher.get_default_bank_cash_account", @@ -261,21 +198,6 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn){ refresh_field('entries'); } -calc_total_advance = function(doc,cdt,cdn) { - var doc = locals[doc.doctype][doc.name]; - var el = getchildren('Purchase Invoice Advance',doc.name,'advance_allocation_details') - var total_advance = 0; - for(var i in el) { - if (! el[i].allocated_amount == 0) { - total_advance += flt(el[i].allocated_amount); - } - } - doc.total_amount_to_pay = flt(doc.grand_total) - flt(doc.write_off_amount); - doc.total_advance = flt(total_advance); - doc.outstanding_amount = flt(doc.total_amount_to_pay) - flt(total_advance); - refresh_many(['total_advance','outstanding_amount', 'total_amount_to_pay']); -} - cur_frm.cscript.make_jv = function(doc, dt, dn, bank_account) { var jv = wn.model.make_new_doc_and_get_name('Journal Voucher'); jv = locals['Journal Voucher'][jv]; diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py index c53b6d94fcc..59e66b57dbf 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -91,6 +91,11 @@ class DocType(BuyingController): msgprint("%s does not have an Account Head in %s. You must first create it from the Supplier Master" % (self.doc.supplier, self.doc.company)) return ret + def set_supplier_defaults(self): + self.doc.fields.update(self.get_cust()) + self.doc.fields.update(self.get_credit_to()) + super(DocType, self).set_supplier_defaults() + def get_cust(self): ret = {} if self.doc.credit_to: @@ -100,31 +105,6 @@ class DocType(BuyingController): return ret - def get_default_values(self, args): - if isinstance(args, basestring): - import json - args = json.loads(args) - - out = webnotes._dict() - - item = webnotes.conn.sql("""select name, purchase_account, cost_center, - is_stock_item from `tabItem` where name=%s""", args.get("item_code"), as_dict=1) - - if item and item[0]: - item = item[0] - - if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) and \ - item.is_stock_item == "Yes": - # unset expense head for stock item and auto inventory accounting - out.expense_head = out.cost_center = None - else: - if not args.get("expense_head"): - out.expense_head = item.purchase_account - if not args.get("cost_center"): - out.cost_center = item.cost_center - - return out - def pull_details(self): if self.doc.purchase_receipt_main: self.validate_duplicate_docname('purchase_receipt') @@ -326,7 +306,7 @@ class DocType(BuyingController): against_accounts.append(stock_not_billed_account) elif not item.expense_head: - msgprint(_("""Expense account is mandatory for item: """) + item.item_code, + msgprint(_("""Expense account is mandatory for item: """) + (item.item_code or item.item_name), raise_exception=1) elif item.expense_head not in against_accounts: @@ -466,9 +446,9 @@ class DocType(BuyingController): # expense will be booked in sales invoice stock_item_and_auto_inventory_accounting = True - valuation_amt = (flt(item.amount, self.precision.item.amount) + - flt(item.item_tax_amount, self.precision.item.item_tax_amount) + - flt(item.rm_supp_cost, self.precision.item.rm_supp_cost)) + valuation_amt = (flt(item.amount, self.precision("amount", item)) + + flt(item.item_tax_amount, self.precision("item_tax_amount", item)) + + flt(item.rm_supp_cost, self.precision("rm_supp_cost", item))) gl_entries.append( self.get_gl_dict({ diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.txt b/accounts/doctype/purchase_invoice/purchase_invoice.txt index 283c6123711..4647fd235d5 100755 --- a/accounts/doctype/purchase_invoice/purchase_invoice.txt +++ b/accounts/doctype/purchase_invoice/purchase_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-07 13:50:30", + "creation": "2013-05-21 16:16:39", "docstatus": 0, - "modified": "2013-05-13 11:12:56", + "modified": "2013-05-28 12:18:35", "modified_by": "Administrator", "owner": "Administrator" }, @@ -89,10 +89,11 @@ "read_only": 0 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "supplier_name", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Name", "oldfieldname": "supplier_name", @@ -100,34 +101,38 @@ "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -392,6 +397,7 @@ "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -399,7 +405,6 @@ "read_only": 0 }, { - "depends_on": "eval:doc.supplier", "doctype": "DocField", "fieldname": "supplier_address", "fieldtype": "Link", @@ -415,7 +420,6 @@ "width": "50%" }, { - "depends_on": "eval:doc.supplier", "doctype": "DocField", "fieldname": "contact_person", "fieldtype": "Link", diff --git a/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/accounts/doctype/purchase_invoice/test_purchase_invoice.py index a70c932d9b2..2ae569b7eb8 100644 --- a/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -24,6 +24,7 @@ from webnotes.utils import cint import webnotes.defaults test_dependencies = ["Item", "Cost Center"] +test_ignore = ["Serial No"] class TestPurchaseInvoice(unittest.TestCase): def test_gl_entries_without_auto_inventory_accounting(self): @@ -119,25 +120,6 @@ class TestPurchaseInvoice(unittest.TestCase): wrapper.insert() wrapper.load_from_db() - self.assertEqual(wrapper.doclist[0].net_total, 1250) - - # tax amounts - expected_values = [ - ["_Test Account Shipping Charges - _TC", 100, 1350], - ["_Test Account Customs Duty - _TC", 125, 1350], - ["_Test Account Excise Duty - _TC", 140, 1490], - ["_Test Account Education Cess - _TC", 2.8, 1492.8], - ["_Test Account S&H Education Cess - _TC", 1.4, 1494.2], - ["_Test Account CST - _TC", 29.88, 1524.08], - ["_Test Account VAT - _TC", 156.25, 1680.33], - ["_Test Account Discount - _TC", 168.03, 1512.30], - ] - - for i, tax in enumerate(wrapper.doclist.get({"parentfield": "purchase_tax_details"})): - self.assertEqual(tax.account_head, expected_values[i][0]) - self.assertEqual(tax.tax_amount, expected_values[i][1]) - self.assertEqual(tax.total, expected_values[i][2]) - expected_values = [ ["_Test Item Home Desktop 100", 90, 59], ["_Test Item Home Desktop 200", 135, 177] @@ -147,13 +129,41 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(item.item_tax_amount, expected_values[i][1]) self.assertEqual(item.valuation_rate, expected_values[i][2]) + self.assertEqual(wrapper.doclist[0].net_total, 1250) + + # tax amounts + expected_values = [ + ["_Test Account Shipping Charges - _TC", 100, 1350], + ["_Test Account Customs Duty - _TC", 125, 1350], + ["_Test Account Excise Duty - _TC", 140, 1490], + ["_Test Account Education Cess - _TC", 2.8, 1492.8], + ["_Test Account S&H Education Cess - _TC", 1.4, 1494.2], + ["_Test Account CST - _TC", 29.88, 1524.08], + ["_Test Account VAT - _TC", 156.25, 1680.33], + ["_Test Account Discount - _TC", 168.03, 1512.30], + ] + + for i, tax in enumerate(wrapper.doclist.get({"parentfield": "purchase_tax_details"})): + self.assertEqual(tax.account_head, expected_values[i][0]) + self.assertEqual(tax.tax_amount, expected_values[i][1]) + self.assertEqual(tax.total, expected_values[i][2]) + def test_purchase_invoice_with_subcontracted_item(self): wrapper = webnotes.bean(copy=test_records[0]) wrapper.doclist[1].item_code = "_Test FG Item" wrapper.run_method("calculate_taxes_and_totals") wrapper.insert() wrapper.load_from_db() - + + expected_values = [ + ["_Test FG Item", 90, 7059], + ["_Test Item Home Desktop 200", 135, 177] + ] + for i, item in enumerate(wrapper.doclist.get({"parentfield": "entries"})): + self.assertEqual(item.item_code, expected_values[i][0]) + self.assertEqual(item.item_tax_amount, expected_values[i][1]) + self.assertEqual(item.valuation_rate, expected_values[i][2]) + self.assertEqual(wrapper.doclist[0].net_total, 1250) # tax amounts @@ -172,15 +182,6 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(tax.account_head, expected_values[i][0]) self.assertEqual(tax.tax_amount, expected_values[i][1]) self.assertEqual(tax.total, expected_values[i][2]) - - expected_values = [ - ["_Test FG Item", 90, 7059], - ["_Test Item Home Desktop 200", 135, 177] - ] - for i, item in enumerate(wrapper.doclist.get({"parentfield": "entries"})): - self.assertEqual(item.item_code, expected_values[i][0]) - self.assertEqual(item.item_tax_amount, expected_values[i][1]) - self.assertEqual(item.valuation_rate, expected_values[i][2]) def test_purchase_invoice_with_advance(self): from accounts.doctype.journal_voucher.test_journal_voucher \ 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 619aed1954c..d6d2a92b7ae 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,8 +1,8 @@ [ { - "creation": "2013-04-19 11:00:06", + "creation": "2013-05-21 16:16:04", "docstatus": 0, - "modified": "2013-05-07 11:23:56", + "modified": "2013-05-28 12:02:02", "modified_by": "Administrator", "owner": "Administrator" }, @@ -101,7 +101,7 @@ "oldfieldname": "tax_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "read_only": 0, + "read_only": 1, "reqd": 0 }, { @@ -159,35 +159,5 @@ "print_hide": 1, "read_only": 0, "search_index": 0 - }, - { - "description": "Cheating Field\nPlease do not delete ", - "doctype": "DocField", - "fieldname": "total_tax_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Total +Tax", - "no_copy": 1, - "oldfieldname": "total_tax_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 0, - "report_hide": 1 - }, - { - "description": "Cheating Field\nPlease do not delete ", - "doctype": "DocField", - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Tax Amount", - "no_copy": 1, - "oldfieldname": "total_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "read_only": 0, - "report_hide": 1 } ] \ No newline at end of file diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index be6ec3d0018..105d350bb21 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -22,52 +22,90 @@ cur_frm.cscript.sales_team_fname = "sales_team"; // print heading cur_frm.pformat.print_heading = 'Invoice'; -wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/selling/doctype/sales_common/sales_common.js'); -// On Load -// ------- -cur_frm.cscript.onload = function(doc,dt,dn) { - cur_frm.cscript.manage_rounded_total(); - if(!doc.customer && doc.debit_to) wn.meta.get_docfield(dt, 'debit_to', dn).print_hide = 0; - if (doc.__islocal) { - if(!doc.due_date) set_multiple(dt,dn,{due_date:get_today()}); - if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()}); - if(!doc.currency && sys_defaults.currency) set_multiple(dt,dn,{currency:sys_defaults.currency}); - if(!doc.price_list_currency) set_multiple(dt, dn, {price_list_currency: doc.currency, plc_conversion_rate: 1}); - - } -} - -cur_frm.cscript.onload_post_render = function(doc, dt, dn) { - var callback = function(doc, dt, dn) { - // called from mapper, update the account names for items and customer - var callback2 = function(doc, dt, dn) { - if(doc.customer && doc.__islocal) { - $c_obj(make_doclist(doc.doctype,doc.name), - 'load_default_accounts','', - function(r,rt) { - refresh_field('entries'); - cur_frm.cscript.customer(doc,dt,dn,onload=true); - } - ); +wn.provide("erpnext.accounts"); +erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({ + onload: function() { + this._super(); + + if(!this.frm.doc.__islocal) { + // show debit_to in print format + if(!this.frm.doc.customer && this.frm.doc.debit_to) { + this.frm.set_df_property("debit_to", "print_hide", 0); } } - // defined in sales_common.js - var callback1 = function(doc, dt, dn) { - //for previously created sales invoice, set required field related to pos - cur_frm.cscript.update_item_details(doc, dt, dn, callback2); + }, + + refresh: function(doc, dt, dn) { + this._super(); + + cur_frm.cscript.is_opening(doc, dt, dn); + + if(doc.docstatus==1) { + cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry); + cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + + if(doc.is_pos==1 && doc.update_stock!=1) + cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']); + + if(doc.outstanding_amount!=0) + cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher); + } + cur_frm.cscript.hide_fields(doc, dt, dn); + }, + + is_pos: function() { + if(cint(this.frm.doc.is_pos)) { + if(!this.frm.doc.company) { + this.frm.set_value("is_pos", 0); + msgprint(wn._("Please specify Company to proceed")); + } else { + var me = this; + this.frm.call({ + doc: me.frm.doc, + method: "set_missing_values", + }); + } } - if(doc.is_pos ==1) cur_frm.cscript.is_pos(doc, dt, dn,callback1); - else cur_frm.cscript.update_item_details(doc, dt, dn, callback2); - } - - cur_frm.cscript.hide_price_list_currency(doc, dt, dn, callback); - -} + // TODO toggle display of fields + }, + + debit_to: function() { + this.customer(); + }, + + allocated_amount: function() { + this.calculate_total_advance("Sales Invoice", "advance_adjustment_details"); + this.frm.refresh_fields(); + }, + + write_off_outstanding_amount_automatically: function() { + if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { + wn.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); + // this will make outstanding amount 0 + this.frm.set_value("write_off_amount", + flt(this.frm.doc.grand_total - this.frm.doc.paid_amount), precision("write_off_amount")); + } + + this.frm.runclientscript("write_off_amount"); + }, + + write_off_amount: function() { + this.calculate_outstanding_amount(); + this.frm.refresh_fields(); + }, + + paid_amount: function() { + this.write_off_outstanding_amount_automatically(); + }, +}); +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm})); // Hide Fields // ------------ @@ -95,8 +133,6 @@ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) { for(f in item_flds_pos) cur_frm.fields_dict['entries'].grid.set_column_disp(item_flds_pos[f], false); } - cur_frm.toggle_display("contact_section", doc.customer); - // India related fields var cp = wn.control_panel; if (cp.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']); @@ -104,50 +140,6 @@ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) { } -// Refresh -// ------- -cur_frm.cscript.refresh = function(doc, dt, dn) { - cur_frm.cscript.is_opening(doc, dt, dn); - erpnext.hide_naming_series(); - - // Show / Hide button - cur_frm.clear_custom_buttons(); - if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, dt, dn); - - if(doc.docstatus==1) { - cur_frm.add_custom_button('View Ledger', cur_frm.cscript.view_ledger_entry); - cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); - - if(doc.is_pos==1 && doc.update_stock!=1) - cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']); - - if(doc.outstanding_amount!=0) - cur_frm.add_custom_button('Make Payment Entry', cur_frm.cscript.make_bank_voucher); - } - cur_frm.cscript.hide_fields(doc, dt, dn); - -} - -//fetch retail transaction related fields -//-------------------------------------------- -cur_frm.cscript.is_pos = function(doc,dt,dn,callback){ - cur_frm.cscript.hide_fields(doc, dt, dn); - if(doc.is_pos == 1){ - if (!doc.company) { - msgprint("Please select company to proceed"); - doc.is_pos = 0; - refresh_field('is_pos'); - } - else { - var callback1 = function(r,rt){ - if(callback) callback(doc, dt, dn); - cur_frm.refresh(); - } - $c_obj(make_doclist(dt,dn),'set_pos_fields','',callback1); - } - } -} - cur_frm.cscript.mode_of_payment = function(doc) { cur_frm.call({ method: "get_bank_cash_account", @@ -159,94 +151,10 @@ cur_frm.cscript.update_stock = function(doc, dt, dn) { cur_frm.cscript.hide_fields(doc, dt, dn); } -cur_frm.cscript.warehouse = function(doc, cdt , cdn) { - var d = locals[cdt][cdn]; - if (!d.item_code) { msgprint("please enter item code first"); return }; - if (d.warehouse) { - arg = "{'item_code':'" + d.item_code + "','warehouse':'" + d.warehouse +"'}"; - get_server_fields('get_actual_qty',arg,'entries',doc,cdt,cdn,1); - } -} - - - -//Customer -cur_frm.cscript.customer = function(doc,dt,dn,onload) { - cur_frm.toggle_display("contact_section", doc.customer); - - var pl = doc.price_list_name; - var callback = function(r,rt) { - var callback2 = function(doc, dt, dn) { - doc = locals[dt][dn]; - if(doc.debit_to && doc.posting_date){ - get_server_fields('get_cust_and_due_date','','',doc,dt,dn,1, - function(doc, dt, dn) { - cur_frm.refresh(); - if (!onload && (pl != doc.price_list_name)) cur_frm.cscript.price_list_name(doc, dt, dn); - }); - - } - } - var doc = locals[cur_frm.doctype][cur_frm.docname]; - get_server_fields('get_debit_to','','',doc, dt, dn, 0, callback2); - } - var args = onload ? 'onload':'' - if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_customer_address', args, callback); - -} - - - cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); } -// Set Due Date = posting date + credit days -cur_frm.cscript.debit_to = function(doc,dt,dn) { - - var callback2 = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - cur_frm.refresh(); - } - - var callback = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - if(doc.customer) $c_obj(make_doclist(dt,dn), 'get_default_customer_address', '', callback2); - cur_frm.toggle_display("contact_section", doc.customer); - - cur_frm.refresh(); - } - - if(doc.debit_to && doc.posting_date){ - get_server_fields('get_cust_and_due_date','','',doc,dt,dn,1,callback); - } -} - - - -//refresh advance amount -//------------------------------------------------- - - -cur_frm.cscript.write_off_outstanding_amount_automatically = function(doc) { - if (doc.write_off_outstanding_amount_automatically == 1) - doc.write_off_amount = flt(doc.grand_total) - flt(doc.paid_amount); - - doc.outstanding_amount = flt(doc.grand_total) - flt(doc.paid_amount) - flt(doc.write_off_amount); - refresh_field(['write_off_amount', 'outstanding_amount']); -} - -cur_frm.cscript.paid_amount = function(doc) { - cur_frm.cscript.write_off_outstanding_amount_automatically(doc); -} - -cur_frm.cscript.write_off_amount = function(doc) { - cur_frm.cscript.write_off_outstanding_amount_automatically(doc); -} - - -//Set debit and credit to zero on adding new row -//---------------------------------------------- cur_frm.fields_dict['entries'].grid.onrowadd = function(doc, cdt, cdn){ cl = getchildren('Sales Invoice Item', doc.name, cur_frm.cscript.fname, doc.doctype); @@ -282,12 +190,6 @@ cur_frm.cscript.get_items = function(doc, dt, dn) { -// Allocated Amount in advances table -// ----------------------------------- -cur_frm.cscript.allocated_amount = function(doc,cdt,cdn){ - cur_frm.cscript.calc_adjustment_amount(doc,cdt,cdn); -} - //Make Delivery Note Button //----------------------------- @@ -452,19 +354,6 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn){ refresh_field(cur_frm.cscript.fname); } -cur_frm.cscript.calc_adjustment_amount = function(doc,cdt,cdn) { - var doc = locals[doc.doctype][doc.name]; - var el = getchildren('Sales Invoice Advance',doc.name,'advance_adjustment_details'); - var total_adjustment_amt = 0 - for(var i in el) { - total_adjustment_amt += flt(el[i].allocated_amount) - } - doc.total_advance = flt(total_adjustment_amt); - doc.outstanding_amount = flt(doc.grand_total) - flt(total_adjustment_amt) - flt(doc.paid_amount) - flt(doc.write_off_amount); - refresh_many(['total_advance','outstanding_amount']); -} - - // Make Journal Voucher // -------------------- cur_frm.cscript.make_jv = function(doc, dt, dn, bank_account) { diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 6871b1e90f9..55b233621be 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -41,7 +41,6 @@ class DocType(SellingController): def validate(self): super(DocType, self).validate() - self.fetch_missing_values() self.validate_posting_time() self.so_dn_required() self.validate_proj_cust() @@ -50,7 +49,6 @@ class DocType(SellingController): sales_com_obj.check_active_sales_items(self) sales_com_obj.check_conversion_rate(self) sales_com_obj.validate_max_discount(self, 'entries') - sales_com_obj.get_allocated_sum(self) sales_com_obj.validate_fiscal_year(self.doc.fiscal_year, self.doc.posting_date,'Posting Date') self.validate_customer() @@ -136,25 +134,16 @@ class DocType(SellingController): self.validate_recurring_invoice() self.convert_to_recurring() - def fetch_missing_values(self): - # fetch contact and address details for customer, if they are not mentioned - if not (self.doc.contact_person and self.doc.customer_address): - for fieldname, val in self.get_default_address_and_contact("customer").items(): - if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): - self.doc.fields[fieldname] = val - - # fetch missing item values - for item in self.doclist.get({"parentfield": "entries"}): - if item.fields.get("item_code"): - ret = get_obj('Sales Common').get_item_details(item.fields, self) - for fieldname, value in ret.items(): - if self.meta.get_field(fieldname, parentfield="entries") and \ - not item.fields.get(fieldname): - item.fields[fieldname] = value + def set_missing_values(self, for_validate=False): + super(DocType, self).set_missing_values(for_validate) + self.set_pos_fields(for_validate) - # fetch pos details, if they are not fetched - if cint(self.doc.is_pos): - self.set_pos_fields(for_validate=True) + def set_customer_defaults(self): + # TODO cleanup these methods + self.doc.fields.update(self.get_debit_to()) + self.get_cust_and_due_date() + + super(DocType, self).set_customer_defaults() def update_time_log_batch(self, sales_invoice): for d in self.doclist.get({"doctype":"Sales Invoice Item"}): @@ -175,10 +164,11 @@ class DocType(SellingController): """Set retail related fields from pos settings""" if cint(self.doc.is_pos) != 1: return + + from selling.utils import get_pos_settings, apply_pos_settings + pos = get_pos_settings(self.doc.company) - if self.pos_settings: - pos = self.pos_settings[0] - + if pos: self.doc.conversion_rate = flt(pos.conversion_rate) if not self.doc.debit_to: @@ -195,7 +185,7 @@ class DocType(SellingController): # set pos values in items for item in self.doclist.get({"parentfield": "entries"}): if item.fields.get('item_code'): - for fieldname, val in self.apply_pos_settings(item.fields).items(): + for fieldname, val in apply_pos_settings(pos, item.fields).items(): if (not for_validate) or (for_validate and not item.fields.get(fieldname)): item.fields[fieldname] = val @@ -205,7 +195,7 @@ class DocType(SellingController): # fetch charges if self.doc.charge and not len(self.doclist.get({"parentfield": "other_charges"})): - self.get_other_charges() + self.set_taxes() def get_customer_account(self): """Get Account Head to which amount needs to be Debited based on Customer""" @@ -275,86 +265,14 @@ class DocType(SellingController): ret = self.get_debit_to() self.doc.debit_to = ret.get('debit_to') - - def load_default_accounts(self): - """ - Loads default accounts from items, customer when called from mapper - """ - self.get_income_expense_account('entries') - - - def get_income_expense_account(self,doctype): - for d in getlist(self.doclist, doctype): - if d.item_code: - item = webnotes.conn.get_value("Item", d.item_code, ["default_income_account", - "default_sales_cost_center", "purchase_account"], as_dict=True) - d.income_account = item['default_income_account'] or "" - d.cost_center = item['default_sales_cost_center'] or "" - - if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) \ - and cint(self.doc.is_pos) and cint(self.doc.update_stock): - d.expense_account = item['purchase_account'] or "" - - def get_item_details(self, args=None): - import json - args = args and json.loads(args) or {} - if args.get('item_code'): - ret = get_obj('Sales Common').get_item_details(args, self) - - if cint(self.doc.is_pos) == 1 and self.pos_settings: - ret = self.apply_pos_settings(args, ret) - - return ret - - elif cint(self.doc.is_pos) == 1 and self.pos_settings: - for doc in self.doclist.get({"parentfield": "entries"}): - if doc.fields.get('item_code'): - ret = self.apply_pos_settings(doc.fields) - for r in ret: - if not doc.fields.get(r): - doc.fields[r] = ret[r] - @property def pos_settings(self): if not hasattr(self, "_pos_settings"): - dtl = webnotes.conn.sql("""select * from `tabPOS Setting` where user = %s - and company = %s""", (webnotes.session['user'], self.doc.company), as_dict=1) - if not dtl: - dtl = webnotes.conn.sql("""select * from `tabPOS Setting` - where ifnull(user,'') = '' and company = %s""", self.doc.company, as_dict=1) - self._pos_settings = dtl + from selling.utils import get_pos_settings + self._pos_settings = get_pos_settings({"company": self.doc.company}) return self._pos_settings - def apply_pos_settings(self, args, ret=None): - if not ret: ret = {} - - pos = self.pos_settings[0] - - item = webnotes.conn.sql("""select default_income_account, default_sales_cost_center, - default_warehouse, purchase_account from tabItem where name = %s""", - args.get('item_code'), as_dict=1) - - if item: - item = item[0] - - ret.update({ - "income_account": item.get("default_income_account") \ - or pos.get("income_account") or args.get("income_account"), - "cost_center": item.get("default_sales_cost_center") \ - or pos.get("cost_center") or args.get("cost_center"), - "warehouse": item.get("default_warehouse") \ - or pos.get("warehouse") or args.get("warehouse"), - "expense_account": item.get("purchase_account") \ - or pos.get("expense_account") or args.get("expense_account") - }) - - if ret.get("warehouse"): - ret["actual_qty"] = flt(webnotes.conn.get_value("Bin", - {"item_code": args.get("item_code"), "warehouse": args.get("warehouse")}, - "actual_qty")) - return ret - def get_barcode_details(self, barcode): return get_obj('Sales Common').get_barcode_details(barcode) @@ -378,14 +296,6 @@ class DocType(SellingController): return get_obj('Sales Common').get_tc_details(self) - def load_default_taxes(self): - self.doclist = get_obj('Sales Common').load_default_taxes(self) - - - def get_other_charges(self): - self.doclist = get_obj('Sales Common').get_other_charges(self) - - def get_advances(self): super(DocType, self).get_advances(self.doc.debit_to, "Sales Invoice Advance", "advance_adjustment_details", "credit") @@ -629,7 +539,9 @@ class DocType(SellingController): else: self.doclist = self.doc.clear_table(self.doclist, 'packing_details') webnotes.conn.set(self.doc,'paid_amount',0) - + + # TODO + # move to calculations webnotes.conn.set(self.doc, 'outstanding_amount', flt(self.doc.grand_total) - flt(self.doc.total_advance) - flt(self.doc.paid_amount) - flt(self.doc.write_off_amount)) @@ -687,15 +599,6 @@ class DocType(SellingController): get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values) - def get_actual_qty(self,args): - args = eval(args) - actual_qty = webnotes.conn.sql("select actual_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (args['item_code'], args['warehouse']), as_dict=1) - ret = { - 'actual_qty' : actual_qty and flt(actual_qty[0]['actual_qty']) or 0 - } - return ret - - def make_gl_entries(self): from accounts.general_ledger import make_gl_entries, merge_similar_entries @@ -740,7 +643,7 @@ class DocType(SellingController): "against": self.doc.debit_to, "credit": flt(tax.tax_amount), "remarks": self.doc.remarks, - "cost_center": tax.cost_center_other_charges + "cost_center": tax.cost_center }) ) diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt index a2c422f38a2..757db0ace59 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.txt +++ b/accounts/doctype/sales_invoice/sales_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-19 11:00:14", + "creation": "2013-05-24 19:29:05", "docstatus": 0, - "modified": "2013-04-22 11:59:28", + "modified": "2013-05-28 18:23:35", "modified_by": "Administrator", "owner": "Administrator" }, @@ -10,6 +10,7 @@ "allow_attach": 1, "autoname": "naming_series:", "doctype": "DocType", + "document_type": "Transaction", "is_submittable": 1, "module": "Accounts", "name": "__common__", @@ -118,10 +119,11 @@ "read_only": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Name", "oldfieldname": "customer_name", @@ -129,34 +131,38 @@ "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -222,11 +228,12 @@ "doctype": "DocField", "fieldname": "entries", "fieldtype": "Table", - "label": "Entries", + "label": "Sales Invoice Items", "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Sales Invoice Item", - "read_only": 0 + "read_only": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -250,35 +257,6 @@ "read_only": 0, "width": "50%" }, - { - "description": "Will be calculated automatically when you enter the details", - "doctype": "DocField", - "fieldname": "net_total", - "fieldtype": "Currency", - "label": "Net Total*", - "oldfieldname": "net_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 0, - "read_only": 1, - "reqd": 1 - }, - { - "doctype": "DocField", - "fieldname": "recalculate_values", - "fieldtype": "Button", - "label": "Re-Calculate Values", - "oldfieldtype": "Button", - "print_hide": 1, - "read_only": 0 - }, - { - "doctype": "DocField", - "fieldname": "col_break25", - "fieldtype": "Column Break", - "read_only": 0, - "width": "50%" - }, { "description": "Select Items from Sales Order", "doctype": "DocField", @@ -312,6 +290,43 @@ "print_hide": 1, "read_only": 0 }, + { + "doctype": "DocField", + "fieldname": "col_break25", + "fieldtype": "Column Break", + "read_only": 0, + "width": "50%" + }, + { + "doctype": "DocField", + "fieldname": "recalculate_values", + "fieldtype": "Button", + "label": "Re-Calculate Values", + "oldfieldtype": "Button", + "print_hide": 1, + "read_only": 0 + }, + { + "doctype": "DocField", + "fieldname": "net_total", + "fieldtype": "Currency", + "label": "Net Total*", + "oldfieldname": "net_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, + { + "doctype": "DocField", + "fieldname": "net_total_export", + "fieldtype": "Currency", + "label": "Net Total (Export)", + "options": "currency", + "print_hide": 0, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "currency_section", @@ -450,6 +465,15 @@ "print_hide": 1, "read_only": 1 }, + { + "doctype": "DocField", + "fieldname": "other_charges_total_export", + "fieldtype": "Currency", + "label": "Total Taxes and Charges (Export)", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "other_charges_calculation", @@ -728,6 +752,7 @@ "read_only": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -1283,6 +1308,17 @@ "read_only": 0, "report_hide": 1 }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "permlevel": 1, + "report": 0, + "role": "Accounts Manager", + "submit": 0, + "write": 0 + }, { "amend": 1, "cancel": 1, @@ -1290,10 +1326,32 @@ "doctype": "DocPerm", "permlevel": 0, "report": 1, + "role": "Accounts Manager", + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 0, + "create": 1, + "doctype": "DocPerm", + "permlevel": 0, + "report": 1, "role": "Accounts User", "submit": 1, "write": 1 }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "permlevel": 1, + "report": 0, + "role": "Accounts User", + "submit": 0, + "write": 0 + }, { "doctype": "DocPerm", "match": "customer", @@ -1301,4 +1359,9 @@ "report": 1, "role": "Customer" }, + { + "doctype": "DocPerm", + "permlevel": 0, + "role": "Retail User" + } ] \ No newline at end of file diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index b46cdd17770..f0ff124898b 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1,12 +1,226 @@ import webnotes -import unittest +import unittest, json +from webnotes.utils import flt class TestSalesInvoice(unittest.TestCase): def make(self): - w = webnotes.bean(webnotes.copy_doclist(test_records[0])) + w = webnotes.bean(copy=test_records[0]) w.insert() w.submit() return w + + def test_sales_invoice_calculation_base_currency(self): + si = webnotes.bean(copy=test_records[2]) + si.run_method("calculate_taxes_and_totals") + si.insert() + + expected_values = { + "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount", + "base_ref_rate", "basic_rate", "amount"], + "_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500], + "_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750], + } + + # check if children are saved + self.assertEquals(len(si.doclist.get({"parentfield": "entries"})), + len(expected_values)-1) + + # check if item values are calculated + for d in si.doclist.get({"parentfield": "entries"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.doc.net_total, 1250) + self.assertEquals(si.doc.net_total_export, 1250) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "total"], + "_Test Account Shipping Charges - _TC": [100, 1350], + "_Test Account Customs Duty - _TC": [125, 1475], + "_Test Account Excise Duty - _TC": [140, 1615], + "_Test Account Education Cess - _TC": [2.8, 1617.8], + "_Test Account S&H Education Cess - _TC": [1.4, 1619.2], + "_Test Account CST - _TC": [32.38, 1651.58], + "_Test Account VAT - _TC": [156.25, 1807.83], + "_Test Account Discount - _TC": [-180.78, 1627.05] + } + + for d in si.doclist.get({"parentfield": "other_charges"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.account_head][i]) + + self.assertEquals(si.doc.grand_total, 1627.05) + self.assertEquals(si.doc.grand_total_export, 1627.05) + + def test_sales_invoice_calculation_export_currency(self): + si = webnotes.bean(copy=test_records[2]) + si.doc.currency = "USD" + si.doc.conversion_rate = 50 + si.doclist[1].export_rate = 1 + si.doclist[1].ref_rate = 1 + si.doclist[2].export_rate = 3 + si.doclist[2].ref_rate = 3 + si.run_method("calculate_taxes_and_totals") + si.insert() + + expected_values = { + "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount", + "base_ref_rate", "basic_rate", "amount"], + "_Test Item Home Desktop 100": [1, 0, 1, 10, 50, 50, 500], + "_Test Item Home Desktop 200": [3, 0, 3, 15, 150, 150, 750], + } + + # check if children are saved + self.assertEquals(len(si.doclist.get({"parentfield": "entries"})), + len(expected_values)-1) + + # check if item values are calculated + for d in si.doclist.get({"parentfield": "entries"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.doc.net_total, 1250) + self.assertEquals(si.doc.net_total_export, 25) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "total"], + "_Test Account Shipping Charges - _TC": [100, 1350], + "_Test Account Customs Duty - _TC": [125, 1475], + "_Test Account Excise Duty - _TC": [140, 1615], + "_Test Account Education Cess - _TC": [2.8, 1617.8], + "_Test Account S&H Education Cess - _TC": [1.4, 1619.2], + "_Test Account CST - _TC": [32.38, 1651.58], + "_Test Account VAT - _TC": [156.25, 1807.83], + "_Test Account Discount - _TC": [-180.78, 1627.05] + } + + for d in si.doclist.get({"parentfield": "other_charges"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.account_head][i]) + + self.assertEquals(si.doc.grand_total, 1627.05) + self.assertEquals(si.doc.grand_total_export, 32.54) + + def test_inclusive_rate_validations(self): + si = webnotes.bean(copy=test_records[2]) + for i, tax in enumerate(si.doclist.get({"parentfield": "other_charges"})): + tax.idx = i+1 + + si.doclist[1].ref_rate = 62.5 + si.doclist[1].ref_rate = 191 + for i in [3, 5, 6, 7, 8, 9]: + si.doclist[i].included_in_print_rate = 1 + + # tax type "Actual" cannot be inclusive + self.assertRaises(webnotes.ValidationError, si.run_method, "calculate_taxes_and_totals") + + # taxes above included type 'On Previous Row Total' should also be included + si.doclist[3].included_in_print_rate = 0 + self.assertRaises(webnotes.ValidationError, si.run_method, "calculate_taxes_and_totals") + + def test_sales_invoice_calculation_base_currency_with_tax_inclusive_price(self): + # prepare + si = webnotes.bean(copy=test_records[3]) + si.run_method("calculate_taxes_and_totals") + si.insert() + + expected_values = { + "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount", + "base_ref_rate", "basic_rate", "amount"], + "_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 50, 50, 500], + "_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 150, 150, 750], + } + + # check if children are saved + self.assertEquals(len(si.doclist.get({"parentfield": "entries"})), + len(expected_values)-1) + + # check if item values are calculated + for d in si.doclist.get({"parentfield": "entries"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.doc.net_total, 1250) + self.assertEquals(si.doc.net_total_export, 1578.3) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "total"], + "_Test Account Excise Duty - _TC": [140, 1390], + "_Test Account Education Cess - _TC": [2.8, 1392.8], + "_Test Account S&H Education Cess - _TC": [1.4, 1394.2], + "_Test Account CST - _TC": [27.88, 1422.08], + "_Test Account VAT - _TC": [156.25, 1578.33], + "_Test Account Customs Duty - _TC": [125, 1703.33], + "_Test Account Shipping Charges - _TC": [100, 1803.33], + "_Test Account Discount - _TC": [-180.33, 1623] + } + + for d in si.doclist.get({"parentfield": "other_charges"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(flt(d.fields.get(k), 6), expected_values[d.account_head][i]) + + self.assertEquals(si.doc.grand_total, 1623) + self.assertEquals(si.doc.grand_total_export, 1623) + + def test_sales_invoice_calculation_export_currency_with_tax_inclusive_price(self): + # prepare + si = webnotes.bean(copy=test_records[3]) + si.doc.currency = "USD" + si.doc.conversion_rate = 50 + si.doclist[1].ref_rate = 55.56 + si.doclist[1].adj_rate = 10 + si.doclist[2].ref_rate = 187.5 + si.doclist[2].adj_rate = 20 + si.doclist[9].rate = 5000 + + si.run_method("calculate_taxes_and_totals") + si.insert() + + expected_values = { + "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount", + "base_ref_rate", "basic_rate", "amount"], + "_Test Item Home Desktop 100": [55.56, 10, 50, 500, 2222.11, 1999.9, 19999.0], + "_Test Item Home Desktop 200": [187.5, 20, 150, 750, 7375.66, 5900.53, 29502.65], + } + + # check if children are saved + self.assertEquals(len(si.doclist.get({"parentfield": "entries"})), + len(expected_values)-1) + + # check if item values are calculated + for d in si.doclist.get({"parentfield": "entries"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(d.fields.get(k), expected_values[d.item_code][i]) + + # check net total + self.assertEquals(si.doc.net_total, 49501.65) + self.assertEquals(si.doc.net_total_export, 1250) + + # check tax calculation + expected_values = { + "keys": ["tax_amount", "total"], + "_Test Account Excise Duty - _TC": [5540.22, 55041.87], + "_Test Account Education Cess - _TC": [110.81, 55152.68], + "_Test Account S&H Education Cess - _TC": [55.4, 55208.08], + "_Test Account CST - _TC": [1104.16, 56312.24], + "_Test Account VAT - _TC": [6187.71, 62499.95], + "_Test Account Customs Duty - _TC": [4950.17, 67450.12], + "_Test Account Shipping Charges - _TC": [5000, 72450.12], + "_Test Account Discount - _TC": [-7245.01, 65205.11] + } + + for d in si.doclist.get({"parentfield": "other_charges"}): + for i, k in enumerate(expected_values["keys"]): + self.assertEquals(flt(d.fields.get(k), 6), expected_values[d.account_head][i]) + + self.assertEquals(si.doc.grand_total, 65205.11) + self.assertEquals(si.doc.grand_total_export, 1304.1) def test_outstanding(self): w = self.make() @@ -103,7 +317,7 @@ class TestSalesInvoice(unittest.TestCase): pos[0]["cash_bank_account"] = "_Test Account Bank Account - _TC" pos[0]["paid_amount"] = 600.0 - si = webnotes.bean(pos) + si = webnotes.bean(copy=pos) si.insert() si.submit() @@ -113,7 +327,7 @@ class TestSalesInvoice(unittest.TestCase): si.doc.name, as_dict=1)[0] self.assertTrue(sle) self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty], - ["_Test Item", "_Test Warehouse", -5.0]) + ["_Test Item", "_Test Warehouse", -1.0]) # check gl entries stock_in_hand_account = webnotes.conn.get_value("Company", "_Test Company", @@ -126,11 +340,11 @@ class TestSalesInvoice(unittest.TestCase): expected_gl_entries = 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_in_hand_account, 0.0, 375.0], - [test_records[1][1]["expense_account"], 375.0, 0.0], + [pos[1]["income_account"], 0.0, 500.0], + [pos[2]["account_head"], 0.0, 80.0], + [pos[3]["account_head"], 0.0, 50.0], + [stock_in_hand_account, 0.0, 75.0], + [pos[1]["expense_account"], 75.0, 0.0], [si.doc.debit_to, 0.0, 600.0], ["_Test Account Bank Account - _TC", 600.0, 0.0] ]) @@ -444,7 +658,7 @@ test_records = [ "description": "VAT", "doctype": "Sales Taxes and Charges", "parentfield": "other_charges", - "tax_amount": 30.0, + "rate": 6, }, { "account_head": "_Test Account Service Tax - _TC", @@ -452,7 +666,7 @@ test_records = [ "description": "Service Tax", "doctype": "Sales Taxes and Charges", "parentfield": "other_charges", - "tax_amount": 31.8, + "rate": 6.36, }, { "parentfield": "sales_team", @@ -494,7 +708,7 @@ test_records = [ "description": "_Test Item", "doctype": "Sales Invoice Item", "parentfield": "entries", - "qty": 5.0, + "qty": 1.0, "basic_rate": 500.0, "amount": 500.0, "export_rate": 500.0, @@ -509,7 +723,7 @@ test_records = [ "description": "VAT", "doctype": "Sales Taxes and Charges", "parentfield": "other_charges", - "tax_amount": 80.0, + "rate": 16, }, { "account_head": "_Test Account Service Tax - _TC", @@ -517,7 +731,270 @@ test_records = [ "description": "Service Tax", "doctype": "Sales Taxes and Charges", "parentfield": "other_charges", - "tax_amount": 50.0, + "rate": 10 } ], + [ + { + "naming_series": "_T-Sales Invoice-", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "debit_to": "_Test Customer - _TC", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total_export": 0, + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", + "price_list_name": "_Test Price List", + "territory": "_Test Territory", + }, + # items + { + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "item_code": "_Test Item Home Desktop 100", + "item_name": "_Test Item Home Desktop 100", + "qty": 10, + "ref_rate": 50, + "export_rate": 50, + "stock_uom": "_Test UOM", + "item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}), + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + + }, + { + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "item_code": "_Test Item Home Desktop 200", + "item_name": "_Test Item Home Desktop 200", + "qty": 5, + "ref_rate": 150, + "export_rate": 150, + "stock_uom": "_Test UOM", + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + + }, + # taxes + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "Actual", + "account_head": "_Test Account Shipping Charges - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Shipping Charges", + "rate": 100 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account Customs Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Customs Duty", + "rate": 10 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account Excise Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "rate": 12 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Amount", + "account_head": "_Test Account Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "rate": 2, + "row_id": 3 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Amount", + "account_head": "_Test Account S&H Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "rate": 1, + "row_id": 3 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Total", + "account_head": "_Test Account CST - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "CST", + "rate": 2, + "row_id": 5 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account VAT - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "rate": 12.5 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Total", + "account_head": "_Test Account Discount - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Discount", + "rate": -10, + "row_id": 7 + }, + ], + [ + { + "naming_series": "_T-Sales Invoice-", + "company": "_Test Company", + "conversion_rate": 1.0, + "currency": "INR", + "debit_to": "_Test Customer - _TC", + "customer": "_Test Customer", + "customer_name": "_Test Customer", + "doctype": "Sales Invoice", + "due_date": "2013-01-23", + "fiscal_year": "_Test Fiscal Year 2013", + "grand_total_export": 0, + "plc_conversion_rate": 1.0, + "posting_date": "2013-01-23", + "price_list_currency": "INR", + "price_list_name": "_Test Price List", + "territory": "_Test Territory", + }, + # items + { + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "item_code": "_Test Item Home Desktop 100", + "item_name": "_Test Item Home Desktop 100", + "qty": 10, + "ref_rate": 62.5, + "export_rate": 62.5, + "stock_uom": "_Test UOM", + "item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}), + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + + }, + { + "doctype": "Sales Invoice Item", + "parentfield": "entries", + "item_code": "_Test Item Home Desktop 200", + "item_name": "_Test Item Home Desktop 200", + "qty": 5, + "ref_rate": 190.66, + "export_rate": 190.66, + "stock_uom": "_Test UOM", + "income_account": "Sales - _TC", + "cost_center": "_Test Cost Center - _TC", + + }, + # taxes + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account Excise Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "rate": 12, + "included_in_print_rate": 1, + "idx": 1 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Amount", + "account_head": "_Test Account Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "rate": 2, + "row_id": 1, + "included_in_print_rate": 1, + "idx": 2 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Amount", + "account_head": "_Test Account S&H Education Cess - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "rate": 1, + "row_id": 1, + "included_in_print_rate": 1, + "idx": 3 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Total", + "account_head": "_Test Account CST - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "CST", + "rate": 2, + "row_id": 3, + "included_in_print_rate": 1, + "idx": 4 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account VAT - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "VAT", + "rate": 12.5, + "included_in_print_rate": 1, + "idx": 5 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Net Total", + "account_head": "_Test Account Customs Duty - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Customs Duty", + "rate": 10, + "idx": 6 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "Actual", + "account_head": "_Test Account Shipping Charges - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Shipping Charges", + "rate": 100, + "idx": 7 + }, + { + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "charge_type": "On Previous Row Total", + "account_head": "_Test Account Discount - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Discount", + "rate": -10, + "row_id": 7, + "idx": 8 + }, + ], ] \ No newline at end of file diff --git a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt index 89c86f82196..014a9f453cc 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-04-19 11:00:07", "docstatus": 0, - "modified": "2013-05-22 12:06:15", + "modified": "2013-05-22 12:07:00", "modified_by": "Administrator", "owner": "Administrator" }, @@ -107,7 +107,7 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 0, + "read_only": 1, "reqd": 0 }, { @@ -163,7 +163,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 0, + "read_only": 1, "reqd": 1, "search_index": 0 }, diff --git a/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt b/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt index 161eb008ea6..ba9f90724a9 100644 --- a/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt +++ b/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:41", + "creation": "2013-04-24 11:39:32", "docstatus": 0, - "modified": "2013-04-17 14:05:18", + "modified": "2013-05-28 11:59:02", "modified_by": "Administrator", "owner": "Administrator" }, @@ -50,7 +50,7 @@ { "default": ":Company", "doctype": "DocField", - "fieldname": "cost_center_other_charges", + "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", "oldfieldname": "cost_center_other_charges", @@ -85,6 +85,7 @@ "oldfieldname": "tax_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", + "read_only": 1, "reqd": 0 }, { @@ -128,34 +129,6 @@ "print_hide": 1, "search_index": 1 }, - { - "description": "Cheating Field\nPlease do not delete ", - "doctype": "DocField", - "fieldname": "total_tax_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Total Tax Amount", - "no_copy": 1, - "oldfieldname": "total_tax_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "report_hide": 1 - }, - { - "description": "Cheating Field\nPlease do not delete ", - "doctype": "DocField", - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Total Amount", - "no_copy": 1, - "oldfieldname": "total_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "print_hide": 1, - "report_hide": 1 - }, { "allow_on_submit": 0, "description": "If checked, the tax amount will be considered as already included in the Print Rate / Print Amount", diff --git a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js index 1e72010f26f..57874273391 100644 --- a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js +++ b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js @@ -140,7 +140,7 @@ cur_frm.fields_dict['other_charges'].grid.get_field("account_head").get_query = return 'SELECT tabAccount.name FROM tabAccount WHERE tabAccount.group_or_ledger="Ledger" AND tabAccount.docstatus != 2 AND tabAccount.account_type in ("Tax", "Chargeable", "Income Account") AND tabAccount.company = "'+doc.company+'" AND tabAccount.name LIKE "%s"' } -cur_frm.fields_dict['other_charges'].grid.get_field("cost_center_other_charges").get_query = function(doc) { +cur_frm.fields_dict['other_charges'].grid.get_field("cost_center").get_query = function(doc) { return 'SELECT `tabCost Center`.`name` FROM `tabCost Center` WHERE `tabCost Center`.`company_name` = "' +doc.company+'" AND `tabCost Center`.%(key)s LIKE "%s" AND `tabCost Center`.`group_or_ledger` = "Ledger" AND `tabCost Center`.`docstatus`!= 2 ORDER BY `tabCost Center`.`name` ASC LIMIT 50'; } diff --git a/accounts/report/purchase_register/purchase_register.py b/accounts/report/purchase_register/purchase_register.py index d4f23927d56..7c4b38671d7 100644 --- a/accounts/report/purchase_register/purchase_register.py +++ b/accounts/report/purchase_register/purchase_register.py @@ -17,6 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import flt +from webnotes import msgprint, _ def execute(filters=None): if not filters: filters = {} @@ -24,6 +25,11 @@ def execute(filters=None): invoice_list = get_invoices(filters) columns, expense_accounts, tax_accounts = get_columns(invoice_list) + + if not invoice_list: + msgprint(_("No record found")) + return columns, invoice_list + invoice_expense_map = get_invoice_expense_map(invoice_list) invoice_tax_map = get_invoice_tax_map(invoice_list) invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) @@ -64,17 +70,18 @@ def get_columns(invoice_list): "Project:Link/Project:80", "Bill No::120", "Bill Date:Date:80", "Remarks::150", "Purchase Order:Link/Purchase Order:100", "Purchase Receipt:Link/Purchase Receipt:100" ] - - expense_accounts = webnotes.conn.sql_list("""select distinct expense_head - from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' - and parent in (%s) order by expense_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + expense_accounts = tax_accounts = [] + if invoice_list: + expense_accounts = webnotes.conn.sql_list("""select distinct expense_head + from `tabPurchase Invoice Item` where docstatus = 1 and ifnull(expense_head, '') != '' + and parent in (%s) order by expense_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) - tax_accounts = webnotes.conn.sql_list("""select distinct account_head - from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' - and docstatus = 1 and ifnull(account_head, '') != '' and parent in (%s) - order by account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' + and docstatus = 1 and ifnull(account_head, '') != '' and parent in (%s) + order by account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) columns = columns + [(account + ":Currency:120") for account in expense_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ @@ -86,7 +93,7 @@ def get_conditions(filters): conditions = "" if filters.get("company"): conditions += " and company=%(company)s" - if filters.get("account"): conditions += " and account = %(account)s" + if filters.get("account"): conditions += " and credit_to = %(account)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" diff --git a/accounts/report/sales_register/sales_register.py b/accounts/report/sales_register/sales_register.py index 3946f0033d7..99057f9a2eb 100644 --- a/accounts/report/sales_register/sales_register.py +++ b/accounts/report/sales_register/sales_register.py @@ -17,6 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes.utils import flt +from webnotes import msgprint, _ def execute(filters=None): if not filters: filters = {} @@ -24,6 +25,10 @@ def execute(filters=None): invoice_list = get_invoices(filters) columns, income_accounts, tax_accounts = get_columns(invoice_list) + if not invoice_list: + msgprint(_("No record found")) + return columns, invoice_list + invoice_income_map = get_invoice_income_map(invoice_list) invoice_tax_map = get_invoice_tax_map(invoice_list) @@ -69,15 +74,17 @@ def get_columns(invoice_list): "Remarks::150", "Sales Order:Link/Sales Order:100", "Delivery Note:Link/Delivery Note:100" ] - income_accounts = webnotes.conn.sql_list("""select distinct income_account - from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) - order by income_account""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + income_accounts = tax_accounts = [] + if invoice_list: + income_accounts = webnotes.conn.sql_list("""select distinct income_account + from `tabSales Invoice Item` where docstatus = 1 and parent in (%s) + order by income_account""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) - tax_accounts = webnotes.conn.sql_list("""select distinct account_head - from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' - and docstatus = 1 and parent in (%s) order by account_head""" % - ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) + tax_accounts = webnotes.conn.sql_list("""select distinct account_head + from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' + and docstatus = 1 and parent in (%s) order by account_head""" % + ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) columns = columns + [(account + ":Currency:120") for account in income_accounts] + \ ["Net Total:Currency:120"] + [(account + ":Currency:120") for account in tax_accounts] + \ @@ -89,19 +96,19 @@ def get_conditions(filters): conditions = "" if filters.get("company"): conditions += " and company=%(company)s" - if filters.get("account"): conditions += " and account = %(account)s" + if filters.get("account"): conditions += " and debit_to = %(account)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" + if filters.get("from_date"): conditions += " and posting_date >= %(from_date)s" + if filters.get("to_date"): conditions += " and posting_date <= %(to_date)s" return conditions def get_invoices(filters): conditions = get_conditions(filters) return webnotes.conn.sql("""select name, posting_date, debit_to, project_name, customer, - remarks, net_total, other_charges_total, grand_total - from `tabSales Invoice` where docstatus = 1 %s - order by posting_date desc, name desc""" % conditions, filters, as_dict=1) + remarks, net_total, other_charges_total, grand_total from `tabSales Invoice` + where docstatus = 1 %s order by posting_date desc, name desc""" % + conditions, filters, as_dict=1) def get_invoice_income_map(invoice_list): income_details = webnotes.conn.sql("""select parent, income_account, sum(amount) as amount diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index dacee80e55d..f0f38671f50 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -20,131 +20,320 @@ // cur_frm.cscript.fname - Details fieldname wn.provide("erpnext.buying"); +wn.require("app/js/transaction.js"); -erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ +erpnext.buying.BuyingController = erpnext.TransactionController.extend({ setup: function() { var me = this; if(this.frm.fields_dict.price_list_name) { - this.frm.fields_dict.price_list_name.get_query = function() { + this.frm.set_query("price_list_name", function() { return repl("select distinct price_list_name from `tabItem Price` \ where buying = 1 and price_list_name like \"%s%%\""); - }; - } - - if(this.frm.fields_dict.price_list_currency) { - this.frm.fields_dict.price_list_currency.get_query = function() { + }); + + this.frm.set_query("price_list_currency", function() { return repl("select distinct ref_currency from `tabItem Price` \ where price_list_name=\"%(price_list_name)s\" and buying = 1 \ and ref_currency like \"%s%%\"", {price_list_name: me.frm.doc.price_list_name}); - }; + }); } + + if(this.frm.fields_dict.supplier) { + this.frm.set_query("supplier", erpnext.utils.supplier_query); + } + + this.frm.set_query("item_code", this.frm.cscript.fname, function() { + if(me.frm.doc.is_subcontracted == "Yes") { + return erpnext.queries.item({'ifnull(tabItem.is_sub_contracted_item, "No")': "Yes"}); + } else { + return erpnext.queries.item({'ifnull(tabItem.is_purchase_item, "No")': "Yes"}); + } + }); }, - refresh: function() { - this.frm.clear_custom_buttons(); - erpnext.hide_naming_series(); - - if(this.frm.fields_dict.supplier) - this.frm.toggle_display("contact_section", this.frm.doc.supplier); - - if(this.frm.fields_dict.currency) - this.set_dynamic_labels(); - }, - - price_list_name: function(callback_fn) { - var me = this; - - if(this.frm.doc.price_list_name) { - if(!this.frm.doc.price_list_currency) { - // set price list currency + supplier: function() { + if(this.frm.doc.supplier || this.frm.doc.credit_to) { + if(!this.frm.doc.company) { + this.frm.set_value("supplier", null); + msgprint(wn._("Please specify Company")); + } else { + var me = this; + var price_list_name = this.frm.doc.price_list_name; + this.frm.call({ - method: "setup.utils.get_price_list_currency", - args: {args: { - price_list_name: this.frm.doc.price_list_name, - use_for: "buying" - }}, + doc: this.frm.doc, + method: "set_supplier_defaults", + freeze: true, callback: function(r) { if(!r.exc) { - me.price_list_currency(); - if (typeof callback_fn === "function") - callback_fn(me.frm.doc, me.frm.doc.doctype, me.frm.doc.name); + me.frm.refresh_fields(); + if(me.frm.doc.price_list_name !== price_list_name) me.price_list_name(); } } }); - } else { - me.price_list_currency(); - if (typeof callback_fn === "function") - callback_fn(me.frm.doc, me.frm.doc.doctype, me.frm.doc.name); } - } + } }, item_code: function(doc, cdt, cdn) { var me = this; - var item = locals[cdt][cdn]; - + var item = wn.model.get_doc(cdt, cdn); if(item.item_code) { + if(!this.validate_company_and_party("supplier")) { + item.item_code = null; + refresh_field("item_code", item.name, item.parentfield); + } else { + this.frm.call({ + method: "buying.utils.get_item_details", + child: item, + args: { + args: { + item_code: item.item_code, + warehouse: item.warehouse, + doctype: me.frm.doc.doctype, + docname: me.frm.doc.name, + supplier: me.frm.doc.supplier, + conversion_rate: me.frm.doc.conversion_rate, + price_list_name: me.frm.doc.price_list_name, + price_list_currency: me.frm.doc.price_list_currency, + plc_conversion_rate: me.frm.doc.plc_conversion_rate, + is_subcontracted: me.frm.doc.is_subcontracted, + company: me.frm.doc.company, + currency: me.frm.doc.currency + } + }, + callback: function(r) { + if(!r.exc) { + me.import_ref_rate(me.frm.doc, cdt, cdn); + } + } + }); + } + } + }, + + price_list_name: function() { + this._super("buying"); + }, + + import_ref_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["import_ref_rate", "discount_rate"]); + + item.import_rate = flt(item.import_ref_rate * (1 - item.discount_rate / 100.0), + precision("import_rate", item)); + + this.calculate_taxes_and_totals(); + }, + + discount_rate: function(doc, cdt, cdn) { + this.import_ref_rate(doc, cdt, cdn); + }, + + import_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["import_rate", "discount_rate"]); + + if(item.import_ref_rate) { + item.discount_rate = flt((1 - item.import_rate / item.import_ref_rate) * 100.0, + precision("discount_rate", item)); + } else { + item.discount_rate = 0.0; + } + + this.calculate_taxes_and_totals(); + }, + + uom: function(doc, cdt, cdn) { + var me = this; + var item = wn.model.get_doc(cdt, cdn); + if(item.item_code && item.uom) { this.frm.call({ - method: "buying.utils.get_item_details", + method: "buying.utils.get_conversion_factor", child: item, args: { - args: { - doctype: me.frm.doc.doctype, - docname: me.frm.doc.name, - item_code: item.item_code, - warehouse: item.warehouse, - supplier: me.frm.doc.supplier, - conversion_rate: me.frm.doc.conversion_rate, - price_list_name: me.frm.doc.price_list_name, - price_list_currency: me.frm.doc.price_list_currency, - plc_conversion_rate: me.frm.doc.plc_conversion_rate - } + item_code: item.item_code, + uom: item.uom }, + callback: function(r) { + if(!r.exc) { + me.conversion_factor(me.frm.doc, cdt, cdn); + } + } }); } }, - update_item_details: function(doc, dt, dn, callback) { - if(!this.frm.doc.__islocal) return; - - var me = this; - var children = getchildren(this.tname, this.frm.doc.name, this.fname); - if(children && children.length) { - this.frm.call({ - doc: me.frm.doc, - method: "update_item_details", - callback: function(r) { - if(!r.exc) { - refresh_field(me.fname); - me.load_defaults(me.frm.doc, dt, dn, callback); - } - } - }) - } else { - this.load_taxes(doc, dt, dn, callback); + qty: function(doc, cdt, cdn) { + this._super(doc, cdt, cdn); + this.conversion_factor(doc, cdt, cdn); + }, + + conversion_factor: function(doc, cdt, cdn) { + if(wn.meta.get_docfield(cdt, "stock_qty", cdn)) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["qty", "conversion_factor"]); + item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item)); + refresh_field("stock_qty", item.name, item.parentfield); } }, - currency: function() { - this.set_dynamic_labels(); + warehouse: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + if(item.item_code && item.warehouse) { + this.frm.call({ + method: "buying.utils.get_conversion_factor", + child: item, + args: { + item_code: item.item_code, + warehouse: item.warehouse + } + }); + } }, - company: function() { - this.set_dynamic_labels(); + project_name: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + if(item.project_name) { + $.each(wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, {parentfield: this.fname}), + function(i, other_item) { + if(!other_item.project_name) { + other_item.project_name = item.project_name; + refresh_field("project_name", other_item.name, other_item.parentfield); + } + }); + } }, - price_list_currency: function() { - this.frm.toggle_reqd("plc_conversion_rate", - !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); + category: function(doc, cdt, cdn) { + // should be the category field of tax table + if(cdt != doc.doctype) { + this.calculate_taxes_and_totals(); + } + }, + + calculate_taxes_and_totals: function() { + this._super(); + this.calculate_total_advance("Purchase Invoice", "advance_allocation_details"); + this.frm.refresh_fields(); + }, + + calculate_item_values: function() { + var me = this; - this.set_dynamic_labels(); - - if(this.frm.doc.price_list_currency === this.get_company_currency()) - this.frm.set_value("plc_conversion_rate", 1.0); - else if(this.frm.doc.price_list_currency === this.frm.doc.currency) - this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate || 1.0); + if(this.frm.doc.doctype != "Purchase Invoice") { + wn.meta.docfield_map[this.tname]["rate"] = $.extend({}, + wn.meta.docfield_map[this.tname]["purchase_rate"]); + } + + $.each(this.frm.item_doclist, function(i, item) { + if(me.frm.doc.doctype != "Purchase Invoice") { + item.rate = item.purchase_rate; + } + + wn.model.round_floats_in(item); + item.import_amount = flt(item.import_rate * item.qty, precision("import_amount", item)); + item.item_tax_amount = 0.0; + + me._set_in_company_currency(item, "import_ref_rate", "purchase_ref_rate"); + me._set_in_company_currency(item, "import_rate", "rate"); + me._set_in_company_currency(item, "import_amount", "amount"); + }); + }, + + calculate_net_total: function() { + var me = this; + + this.frm.doc.net_total = this.frm.doc.net_total_import = 0.0; + $.each(this.frm.item_doclist, function(i, item) { + me.frm.doc.net_total += item.amount; + me.frm.doc.net_total_import += item.import_amount; + }); + + wn.model.round_floats_in(this.frm.doc, ["net_total", "net_total_import"]); + }, + + calculate_totals: function() { + var tax_count = this.frm.tax_doclist.length; + this.frm.doc.grand_total = flt( + tax_count ? this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total, + precision("grand_total")); + this.frm.doc.grand_total_import = flt(this.frm.doc.grand_total / this.frm.doc.conversion_rate, + precision("grand_total_import")); + + this.frm.doc.total_tax = flt(this.frm.doc.grand_total - this.frm.doc.net_total, + precision("total_tax")); + + // rounded totals + if(wn.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) { + this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total); + } + + if(wn.meta.get_docfield(this.frm.doc.doctype, "rounded_total_import", this.frm.doc.name)) { + this.frm.doc.rounded_total_import = Math.round(this.frm.doc.grand_total_import); + } + + // other charges added/deducted + if(tax_count) { + this.frm.doc.other_charges_added = wn.utils.sum($.map(this.frm.tax_doclist, + function(tax) { return tax.add_deduct_tax == "Add" ? tax.tax_amount : 0.0; })); + + this.frm.doc.other_charges_deducted = wn.utils.sum($.map(this.frm.tax_doclist, + function(tax) { return tax.add_deduct_tax == "Deduct" ? tax.tax_amount : 0.0; })); + + wn.model.round_floats_in(this.frm.doc, ["other_charges_added", "other_charges_deducted"]); + + this.frm.doc.other_charges_added_import = flt(this.frm.doc.other_charges_added / this.frm.doc.conversion_rate, + precision("other_charges_added_import")); + this.frm.doc.other_charges_deducted_import = flt(this.frm.doc.other_charges_deducted / this.frm.doc.conversion_rate, + precision("other_charges_deducted_import")); + } + }, + + _cleanup: function() { + this._super(); + this.frm.doc.in_words = this.frm.doc.in_words_import = ""; + + // except in purchase invoice, rate field is purchase_rate + // reset fieldname of rate + if(this.frm.doc.doctype != "Purchase Invoice") { + delete wn.meta.docfield_map[this.tname]["rate"]; + + $.each(this.frm.item_doclist, function(i, item) { + item.purchase_rate = item.rate; + delete item["rate"]; + }); + } + }, + + calculate_outstanding_amount: function() { + if(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.docstatus < 2) { + wn.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]); + this.frm.doc.total_amount_to_pay = flt(this.frm.doc.grand_total - this.frm.doc.write_off_amount, + precision("total_amount_to_pay")); + this.frm.doc.outstanding_amount = flt(this.frm.doc.total_amount_to_pay - this.frm.doc.total_advance, + precision("outstanding_amount")); + } + }, + + set_item_tax_amount: function(item, tax, current_tax_amount) { + // item_tax_amount is the total tax amount applied on that item + // stored for valuation + // + // TODO: rename item_tax_amount to valuation_tax_amount + if(["Valuation", "Valuation and Total"].indexOf(tax.category) != -1) { + // accumulate only if tax is for Valuation / Valuation and Total + item.item_tax_amount += flt(current_tax_amount, precision("item_tax_amount", item)); + } + }, + + show_item_wise_taxes: function() { + if(this.frm.fields_dict.tax_calculation) { + $(this.get_item_wise_taxes_html()) + .appendTo($(this.frm.fields_dict.tax_calculation.wrapper).empty()); + } }, set_dynamic_labels: function(doc, dt, dn) { @@ -162,11 +351,12 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ $.each(fields_list, function(i, fname) { var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname); if(docfield) { - var label = wn._((docfield.label || "")).replace(/\([^\)]*\)/g, ""); + var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); field_label_map[fname] = label.trim() + " (" + currency + ")"; } }); - } + }; + setup_field_label_map(["net_total", "total_tax", "grand_total", "in_words", "other_charges_added", "other_charges_deducted", @@ -188,6 +378,9 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ this.frm.toggle_display(["conversion_rate", "net_total", "grand_total", "in_words", "other_charges_added", "other_charges_deducted"], this.frm.doc.currency != company_currency); + + this.frm.toggle_display(["plc_conversion_rate"], + this.frm.price_list_currency != company_currency); // set labels $.each(field_label_map, function(fname, label) { @@ -204,11 +397,12 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ $.each(fields_list, function(i, fname) { var docfield = wn.meta.get_docfield(grid_doctype, fname); if(docfield) { + var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); field_label_map[grid_doctype + "-" + fname] = - docfield.label + " (" + currency + ")"; + label.trim() + " (" + currency + ")"; } }); - } + }; setup_field_label_map(["purchase_rate", "purchase_ref_rate", "amount", "rate"], company_currency, this.fname); @@ -216,7 +410,9 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ setup_field_label_map(["import_rate", "import_ref_rate", "import_amount"], this.frm.doc.currency, this.fname); - setup_field_label_map(["tax_amount", "total"], company_currency, this.other_fname); + if(this.frm.fields_dict[this.other_fname]) { + setup_field_label_map(["tax_amount", "total"], company_currency, this.other_fname); + } if(this.frm.fields_dict["advance_allocation_details"]) { setup_field_label_map(["advance_amount", "allocated_amount"], company_currency, @@ -236,10 +432,6 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ $.each(field_label_map, function(fname, label) { $wrapper.find('[data-grid-fieldname="'+fname+'"]').text(label); }); - }, - - get_company_currency: function() { - return erpnext.get_currency(this.frm.doc.company); } }); @@ -254,547 +446,4 @@ $.extend(cur_frm.cscript, prev_cscript); var tname = cur_frm.cscript.tname; -var fname = cur_frm.cscript.fname; - -cur_frm.cscript.get_default_schedule_date = function(doc) { - var ch = getchildren( tname, doc.name, fname); - if (flt(ch.length) > 0){ - $c_obj(make_doclist(doc.doctype, doc.name), 'get_default_schedule_date', '', function(r, rt) { refresh_field(fname); }); - } -} - -cur_frm.cscript.load_taxes = function(doc, cdt, cdn, callback) { - // run if this is not executed from dt_map... - doc = locals[doc.doctype][doc.name]; - if(doc.supplier || getchildren('Purchase Taxes and Charges', doc.name, 'purchase_tax_details', doc.doctype).length) { - if(callback) { - callback(doc, cdt, cdn); - } - } else { - $c_obj(make_doclist(doc.doctype, doc.name),'load_default_taxes','',function(r,rt){ - refresh_field('purchase_tax_details'); - if(callback) callback(doc, cdt, cdn); - }); - } -} - - - -// Gets called after existing item details are update to fill in -// remaining default values -cur_frm.cscript.load_defaults = function(doc, dt, dn, callback) { - if(!cur_frm.doc.__islocal) { return; } - - doc = locals[doc.doctype][doc.name]; - var fields_to_refresh = wn.model.set_default_values(doc); - if(fields_to_refresh) { refresh_many(fields_to_refresh); } - - fields_to_refresh = null; - var children = getchildren(cur_frm.cscript.tname, doc.name, cur_frm.cscript.fname); - if(!children) { return; } - for(var i=0; i Check if qty , uom, conversion_factor - if (d.qty && d.uom && d.conversion_factor){ - // Step 2:=> Set stock_qty = qty * conversion_factor - d.stock_qty = flt(flt(d.qty) * flt(d.conversion_factor)); - // Step 3:=> Refer stock_qty field a that particular row. - refresh_field('stock_qty' , d.name,fname); - } -} - -//==================== UOM ====================================================================== -cur_frm.cscript.uom = function(doc, cdt, cdn, args) { - if(!args) args = {}; - - // args passed can contain conversion_factor - var d = locals[cdt][cdn]; - $.extend(args, { - item_code: d.item_code, - uom: d.uom, - stock_qty: flt(d.stock_qty), - }); - - if(d.item_code && d.uom) { - cur_frm.call({ - method: "buying.doctype.purchase_common.purchase_common.get_uom_details", - args: { args: args }, - child: d, - callback: function(r) { - cur_frm.cscript.calc_amount(doc, 2); - } - }); - } -} - - -//==================== Conversion factor ========================================================= -cur_frm.cscript.conversion_factor = function(doc, cdt, cdn) { - var item = locals[cdt][cdn]; - - cur_frm.cscript.uom(doc, cdt, cdn, { conversion_factor: item.conversion_factor }); -} - -//==================== stock qty ====================================================================== -cur_frm.cscript.stock_qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - if(d.uom && d.qty){ - d.conversion_factor = flt(d.stock_qty)/flt(d.qty); - refresh_field('conversion_factor', d.name, fname); - } -} - -//==================== Warehouse ================================================================ -cur_frm.cscript.warehouse = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - if (d.item_code && d.warehouse) { - str_arg = "{'item_code':'" + (d.item_code?d.item_code:'') + "', 'warehouse':'" + (d.warehouse?d.warehouse:'') + "'}" - get_server_fields('get_bin_details', str_arg, fname, doc, cdt, cdn, 1); - } -} - -//=================== Quantity =================================================================== -cur_frm.cscript.qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - // Step 1: => Update Stock Qty - cur_frm.cscript.update_stock_qty(doc,cdt,cdn); - // Step 2: => Calculate Amount - cur_frm.cscript.calc_amount(doc, 2); -} - - -//=================== Purchase Rate ============================================================== -cur_frm.cscript.purchase_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 2); -} - -//==================== Import Rate ================================================================ -cur_frm.cscript.import_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 1); -} - -//==================== Discount Rate ================================================================ -cur_frm.cscript.discount_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 4); -} -//==================== Purchase Ref Rate ================================================================ -cur_frm.cscript.purchase_ref_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 4); -} -//==================== Import Ref Rate ================================================================ -cur_frm.cscript.import_ref_rate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 5); -} - -//==================== Validate ==================================================================== -cur_frm.cscript.validate = function(doc, cdt, cdn) { - cur_frm.cscript.calc_amount(doc, 1); - - // calculate advances if pv - if(doc.docstatus == 0 && doc.doctype == 'Purchase Invoice') calc_total_advance(doc, cdt, cdn); -} - -// **************** RE-CALCULATE VALUES *************************** - -cur_frm.cscript.recalculate_values = function(doc, cdt, cdn) { - cur_frm.cscript.calculate_tax(doc,cdt,cdn); -} - -cur_frm.cscript.calculate_tax = function(doc, cdt, cdn) { - var other_fname = cur_frm.cscript.other_fname; - - var cl = getchildren('Purchase Taxes and Charges', doc.name, other_fname, doc.doctype); - for(var i = 0; i 0) { - var cl = getchildren('Purchase Taxes and Charges', doc.name, other_fname,doc.doctype); - for(var i = 0; i2) alert("You cannot enter more than 2 nos. for division"); - var id1 = cint(row[0].replace(/^\s+|\s+$/g,"")); - var id2 = cint(row[1].replace(/^\s+|\s+$/g,"")); - tax_amount = flt(tax[id1-1].total_amount) / flt(tax[id2-1].total_amount); - } - return tax_amount - } - else if(tax[t].charge_type == 'On Previous Row Total') { - var row = cint(tax[t].row_id); - if(tax[row-1].add_deduct_tax == 'Add'){ - return tax_amount = flt(rate) * (flt(tax[row-1].total_tax_amount)+flt(tax[row-1].total_amount)) / 100; - } - else if(tax[row-1].add_deduct_tax == 'Deduct'){ - return tax_amount = flt(rate) * (flt(tax[row-1].total_tax_amount)-flt(tax[row-1].total_amount)) / 100; - } - } -} - -// ******* Calculation of total amounts of document (item amount + other charges)**************** -cur_frm.cscript.calc_doc_values = function(doc, tname, fname, other_fname) { - doc = locals[doc.doctype][doc.name]; - var net_total = 0; var total_tax = 0; var other_charges_added = 0; - var other_charges_deducted = 0; - var cl = getchildren(tname, doc.name, fname); - for(var i = 0; i now())" % cstr(d.item_code) , as_dict = 1) - ltd = item and cint(item[0]['lead_time_days']) or 0 - if ltd and obj.doc.transaction_date: - if d.fields.has_key('lead_time_date') or obj.doc.doctype == 'Material Request': - d.lead_time_date = cstr(add_days( obj.doc.transaction_date, cint(ltd))) - if not d.fields.has_key('prevdoc_docname') or (d.fields.has_key('prevdoc_docname') and not d.prevdoc_docname): - d.schedule_date = cstr( add_days( obj.doc.transaction_date, cint(ltd))) - # Client Trigger functions #------------------------------------------------------------------------------------------------ @@ -447,28 +436,4 @@ class DocType(BuyingController): for d in getlist(obj.doclist, obj.fname): if d.prevdoc_doctype and d.prevdoc_docname: dt = sql("select transaction_date from `tab%s` where name = '%s'" % (d.prevdoc_doctype, d.prevdoc_docname)) - d.prevdoc_date = dt and dt[0][0].strftime('%Y-%m-%d') or '' - -@webnotes.whitelist() -def get_uom_details(args=None): - """fetches details on change of UOM""" - if not args: - return {} - - if isinstance(args, basestring): - import json - args = json.loads(args) - - uom = webnotes.conn.sql("""select conversion_factor - from `tabUOM Conversion Detail` where parent = %s and uom = %s""", - (args['item_code'], args['uom']), as_dict=1) - - if not uom: return {} - - conversion_factor = args.get("conversion_factor") or \ - flt(uom[0]["conversion_factor"]) - - return { - "conversion_factor": conversion_factor, - "qty": flt(args["stock_qty"]) / conversion_factor, - } + d.prevdoc_date = dt and dt[0][0].strftime('%Y-%m-%d') or '' \ No newline at end of file diff --git a/buying/doctype/purchase_order/purchase_order.js b/buying/doctype/purchase_order/purchase_order.js index afe4741c862..64aa9136a48 100644 --- a/buying/doctype/purchase_order/purchase_order.js +++ b/buying/doctype/purchase_order/purchase_order.js @@ -21,8 +21,8 @@ cur_frm.cscript.fname = "po_details"; cur_frm.cscript.other_fname = "purchase_tax_details"; wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); -wn.require('app/buying/doctype/purchase_common/purchase_common.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/buying/doctype/purchase_common/purchase_common.js'); erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({ refresh: function(doc, cdt, cdn) { @@ -39,54 +39,10 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( cur_frm.add_custom_button('Unstop Purchase Order', cur_frm.cscript['Unstop Purchase Order']); }, - - onload_post_render: function(doc, dt, dn) { - var me = this; - var callback1 = function(doc, dt, dn) { - var callback2 = function(doc, dt, dn) { - if(doc.__islocal) cur_frm.cscript.get_default_schedule_date(doc); - } - me.update_item_details(doc, dt, dn, callback2); - } - - // TODO: improve this - if(this.frm.doc.__islocal) { - if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) { - this.price_list_name(callback1); - } else { - callback1(doc, dt, dn); - } - } - } - }); -var new_cscript = new erpnext.buying.PurchaseOrderController({frm: cur_frm}); - // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new_cscript); - -cur_frm.cscript.onload = function(doc, cdt, cdn) { - // set missing values in parent doc - set_missing_values(doc, { - fiscal_year: sys_defaults.fiscal_year, - conversion_rate: 1, - currency: sys_defaults.currency, - status: "Draft", - transaction_date: get_today(), - is_subcontracted: "No" - }); -} - -cur_frm.cscript.supplier = function(doc,dt,dn) { - if (doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }),'', doc, dt, dn, 1, function() { - cur_frm.refresh(); - }); - $(cur_frm.fields_dict.contact_section.row.wrapper).toggle(true); - } -} +$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm})); cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.supplier) get_server_fields('get_supplier_address', JSON.stringify({supplier: doc.supplier, address: doc.supplier_address, contact: doc.contact_person}),'', doc, dt, dn, 1); @@ -111,10 +67,6 @@ cur_frm.fields_dict.contact_person.on_new = function(dn) { locals['Contact'][dn].supplier_name = locals[cur_frm.doctype][cur_frm.docname].supplier_name; } -cur_frm.cscript.transaction_date = function(doc,cdt,cdn){ - if(doc.__islocal){ cur_frm.cscript.get_default_schedule_date(doc); } -} - cur_frm.fields_dict['po_details'].grid.get_field('project_name').get_query = function(doc, cdt, cdn) { return 'SELECT `tabProject`.name FROM `tabProject` \ WHERE `tabProject`.status not in ("Completed", "Cancelled") \ diff --git a/buying/doctype/purchase_order/purchase_order.py b/buying/doctype/purchase_order/purchase_order.py index 218d4bfeee0..bf59389213e 100644 --- a/buying/doctype/purchase_order/purchase_order.py +++ b/buying/doctype/purchase_order/purchase_order.py @@ -65,10 +65,6 @@ class DocType(BuyingController): self.validate_for_subcontracting() self.update_raw_materials_supplied("po_raw_material_details") - - def get_default_schedule_date(self): - get_obj(dt = 'Purchase Common').get_default_schedule_date(self) - def validate_fiscal_year(self): get_obj(dt = 'Purchase Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.transaction_date,'PO Date') @@ -101,7 +97,6 @@ class DocType(BuyingController): """[['Supplier Quotation', 'Purchase Order'], ['Supplier Quotation Item', 'Purchase Order Item'], ['Purchase Taxes and Charges', 'Purchase Taxes and Charges']]""") - self.get_default_schedule_date() for d in getlist(self.doclist, 'po_details'): if d.prevdoc_detail_docname and not d.schedule_date: d.schedule_date = webnotes.conn.get_value("Material Request Item", diff --git a/buying/doctype/purchase_order/purchase_order.txt b/buying/doctype/purchase_order/purchase_order.txt index 8a56f26e9c0..902329b0502 100644 --- a/buying/doctype/purchase_order/purchase_order.txt +++ b/buying/doctype/purchase_order/purchase_order.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-25 16:01:24", + "creation": "2013-05-21 16:16:39", "docstatus": 0, - "modified": "2013-02-18 13:37:11", + "modified": "2013-05-28 12:20:33", "modified_by": "Administrator", "owner": "Administrator" }, @@ -65,43 +65,48 @@ "search_index": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "supplier_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Name", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -554,6 +559,7 @@ "oldfieldtype": "Text Editor" }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -808,7 +814,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material User", @@ -831,7 +836,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Purchase Manager", @@ -854,7 +858,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "permlevel": 0, "report": 1, "role": "Purchase User", @@ -866,7 +869,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "All", "submit": 0 diff --git a/buying/doctype/purchase_order_item/purchase_order_item.txt b/buying/doctype/purchase_order_item/purchase_order_item.txt index 01a144a1433..5d69fb057dd 100755 --- a/buying/doctype/purchase_order_item/purchase_order_item.txt +++ b/buying/doctype/purchase_order_item/purchase_order_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-07 11:42:55", + "creation": "2013-05-24 19:29:06", "docstatus": 0, - "modified": "2013-05-22 11:59:52", + "modified": "2013-05-28 12:13:21", "modified_by": "Administrator", "owner": "Administrator" }, @@ -361,7 +361,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "width": "100px" }, { diff --git a/buying/doctype/supplier_quotation/supplier_quotation.js b/buying/doctype/supplier_quotation/supplier_quotation.js index 1e4f6cbfb7d..20a20ac5a54 100644 --- a/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/buying/doctype/supplier_quotation/supplier_quotation.js @@ -26,48 +26,15 @@ wn.require('app/buying/doctype/purchase_common/purchase_common.js'); erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({ refresh: function() { this._super(); - if (this.frm.doc.docstatus === 1) { cur_frm.add_custom_button("Make Purchase Order", cur_frm.cscript.make_purchase_order); } - }, - - onload_post_render: function(doc, dt, dn) { - var me = this; - var callback = function(doc, dt, dn) { - cur_frm.cscript.load_taxes(me.frm.doc); - } - - // TODO: improve this - if(this.frm.doc.__islocal) { - if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) { - this.price_list_name(callback); - } else { - callback(doc, dt, dn); - } - } - } - + }, }); -var new_cscript = new erpnext.buying.SupplierQuotationController({frm: cur_frm}); - // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new_cscript); - - -cur_frm.cscript.onload = function(doc, dt, dn) { - // set missing values in parent doc - set_missing_values(doc, { - fiscal_year: sys_defaults.fiscal_year, - conversion_rate: 1, - currency: sys_defaults.currency, - status: "Draft", - transaction_date: get_today(), - is_subcontracted: "No" - }); -} +$.extend(cur_frm.cscript, new erpnext.buying.SupplierQuotationController({frm: cur_frm})); cur_frm.cscript.make_purchase_order = function() { var new_po_name = wn.model.make_new_doc_and_get_name("Purchase Order"); @@ -82,15 +49,6 @@ cur_frm.cscript.make_purchase_order = function() { }, function(r, rt) { loaddoc("Purchase Order", new_po_name) }); } -cur_frm.cscript.supplier = function(doc, dt, dn) { - if (doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }), '', doc, dt, dn, 1, - function() { cur_frm.refresh(); }); - cur_frm.cscript.toggle_contact_section(doc); - } -} - cur_frm.cscript.uom = function(doc, cdt, cdn) { // no need to trigger updation of stock uom, as this field doesn't exist in supplier quotation } diff --git a/buying/doctype/supplier_quotation/supplier_quotation.txt b/buying/doctype/supplier_quotation/supplier_quotation.txt index dace56a43f5..37e0e3c01cf 100644 --- a/buying/doctype/supplier_quotation/supplier_quotation.txt +++ b/buying/doctype/supplier_quotation/supplier_quotation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-25 16:01:25", + "creation": "2013-05-21 16:16:45", "docstatus": 0, - "modified": "2013-02-18 13:40:17", + "modified": "2013-05-28 12:19:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -65,43 +65,48 @@ "search_index": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "supplier_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Name", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -520,6 +525,7 @@ "oldfieldtype": "Text Editor" }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -723,7 +729,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "All", diff --git a/buying/utils.py b/buying/utils.py index 0431e642cb7..1aaa5d11c95 100644 --- a/buying/utils.py +++ b/buying/utils.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ from webnotes.utils import getdate, flt, add_days import json @@ -29,7 +30,11 @@ def get_item_details(args): "warehouse": None, "supplier": None, "transaction_date": None, - "conversion_rate": 1.0 + "conversion_rate": 1.0, + "price_list_name": None, + "price_list_currency": None, + "plc_conversion_rate": 1.0, + "is_subcontracted": "Yes" / "No" } """ if isinstance(args, basestring): @@ -37,86 +42,102 @@ def get_item_details(args): args = webnotes._dict(args) - item_wrapper = webnotes.bean("Item", args.item_code) - item = item_wrapper.doc + item_bean = webnotes.bean("Item", args.item_code) + item = item_bean.doc - from stock.utils import validate_end_of_life - validate_end_of_life(item.name, item.end_of_life) + _validate_item_details(args, item) - # fetch basic values - out = webnotes._dict() - out.update({ - "item_name": item.item_name, - "item_group": item.item_group, - "brand": item.brand, - "description": item.description, - "qty": 0, - "stock_uom": item.stock_uom, - "uom": item.stock_uom, - "conversion_factor": 1.0, - "warehouse": args.warehouse or item.default_warehouse, - "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in - item_wrapper.doclist.get({"parentfield": "item_tax"})))), - "batch_no": None, - "expense_head": item.purchase_account, - "cost_center": item.cost_center - }) + out = _get_basic_details(args, item_bean) - if args.supplier: - item_supplier = item_wrapper.doclist.get({"parentfield": "item_supplier_details", - "supplier": args.supplier}) - if item_supplier: - out["supplier_part_no"] = item_supplier[0].supplier_part_no + out.supplier_part_no = _get_supplier_part_no(args, item_bean) if out.warehouse: - out.projected_qty = webnotes.conn.get_value("Bin", {"item_code": item.name, - "warehouse": out.warehouse}, "projected_qty") + out.projected_qty = get_projected_qty(item.name, out.warehouse) if args.transaction_date and item.lead_time_days: out.schedule_date = out.lead_time_date = add_days(args.transaction_date, item.lead_time_days) - # set zero - out.purchase_ref_rate = out.discount_rate = out.purchase_rate = \ - out.import_ref_rate = out.import_rate = 0.0 + meta = webnotes.get_doctype(args.doctype) + + if meta.get_field("currency"): + out.purchase_ref_rate = out.discount_rate = out.purchase_rate = \ + out.import_ref_rate = out.import_rate = 0.0 + out.update(_get_price_list_rate(args, item_bean, meta)) + + if args.doctype == "Material Request": + out.min_order_qty = flt(item.min_order_qty) - if args.doctype in ["Purchase Order", "Purchase Invoice", "Purchase Receipt", - "Supplier Quotation"]: - # try fetching from price list - if args.price_list_name and args.price_list_currency: - rates_as_per_price_list = get_rates_as_per_price_list(args, item_wrapper.doclist) - if rates_as_per_price_list: - out.update(rates_as_per_price_list) - - # if not found, fetch from last purchase transaction - if not out.purchase_rate: - last_purchase = get_last_purchase_details(item.name, args.docname, args.conversion_rate) - if last_purchase: - out.update(last_purchase) - return out - -def get_rates_as_per_price_list(args, item_doclist=None): - if not item_doclist: - item_doclist = webnotes.bean("Item", args.item_code).doclist - result = item_doclist.get({"parentfield": "ref_rate_details", - "price_list_name": args.price_list_name, "ref_currency": args.price_list_currency, - "buying": 1}) +def _get_basic_details(args, item_bean): + item = item_bean.doc + + out = webnotes._dict({ + "description": item.description_html or item.description, + "qty": 0.0, + "uom": item.stock_uom, + "conversion_factor": 1.0, + "warehouse": args.warehouse or item.default_warehouse, + "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in + item_bean.doclist.get({"parentfield": "item_tax"})))), + "batch_no": None, + "expense_head": item.purchase_account, + "cost_center": item.cost_center + }) + + for fieldname in ("item_name", "item_group", "brand", "stock_uom"): + out[fieldname] = item.fields.get(fieldname) + + return out + +def _get_price_list_rate(args, item_bean, meta): + from utilities.transaction_base import validate_currency + item = item_bean.doc + out = webnotes._dict() + + # try fetching from price list + if args.price_list_name and args.price_list_currency: + price_list_rate = item_bean.doclist.get({ + "parentfield": "ref_rate_details", + "price_list_name": args.price_list_name, + "ref_currency": args.price_list_currency, + "buying": 1}) + if price_list_rate: + out.import_ref_rate = \ + flt(price_list_rate[0].ref_rate * args.plc_conversion_rate / args.conversion_rate) - if result: - purchase_ref_rate = flt(result[0].ref_rate) * flt(args.plc_conversion_rate) - conversion_rate = flt(args.conversion_rate) or 1.0 - return webnotes._dict({ - "purchase_ref_rate": purchase_ref_rate, - "purchase_rate": purchase_ref_rate, - "rate": purchase_ref_rate, - "discount_rate": 0, - "import_ref_rate": purchase_ref_rate / conversion_rate, - "import_rate": purchase_ref_rate / conversion_rate - }) - else: - return webnotes._dict() + # if not found, fetch from last purchase transaction + if not out.import_ref_rate: + last_purchase = get_last_purchase_details(item.name, args.docname, args.conversion_rate) + if last_purchase: + out.update(last_purchase) + + if out.import_ref_rate or out.import_rate: + validate_currency(args, item, meta) + + return out + +def _get_supplier_part_no(args, item_bean): + item_supplier = item_bean.doclist.get({"parentfield": "item_supplier_details", + "supplier": args.supplier}) + + return item_supplier and item_supplier[0].supplier_part_no or None + +def _validate_item_details(args, item): + from utilities.transaction_base import validate_item_fetch + validate_item_fetch(args, item) + + # validate if purchase item or subcontracted item + if item.is_purchase_item != "Yes": + msgprint(_("Item") + (" %s: " % item.name) + _("not a purchase item"), + raise_exception=True) + + if args.is_subcontracted == "Yes" and item.is_sub_contracted_item != "Yes": + msgprint(_("Item") + (" %s: " % item.name) + + _("not a sub-contracted item.") + + _("Please select a sub-contracted item or do not sub-contract the transaction."), + raise_exception=True) def get_last_purchase_details(item_code, doc_name, conversion_rate=1.0): """returns last purchase details in stock uom""" @@ -177,4 +198,14 @@ def get_last_purchase_details(item_code, doc_name, conversion_rate=1.0): "rate": out.purchase_rate }) - return out \ No newline at end of file + return out + +@webnotes.whitelist() +def get_conversion_factor(item_code, uom): + return {"conversion_factor": webnotes.conn.get_value("UOM Conversion Detail", + {"parent": item_code, "uom": uom})} + +@webnotes.whitelist() +def get_projected_qty(item_code, warehouse): + return webnotes.conn.get_value("Bin", {"item_code": item_code, + "warehouse": warehouse}, "projected_qty") \ No newline at end of file diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index 04e4bbdaee3..d7c9441e8cd 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -16,14 +16,260 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import flt - -from utilities.transaction_base import TransactionBase +from webnotes import _, msgprint +from webnotes.utils import flt, cint +from setup.utils import get_company_currency, get_price_list_currency +from utilities.transaction_base import TransactionBase, validate_conversion_rate +import json class AccountsController(TransactionBase): def validate(self): - if self.meta.get_field("grand_total"): + self.set_missing_values(for_validate=True) + + if self.meta.get_field("currency"): + self.company_currency = get_company_currency(self.doc.company) + + validate_conversion_rate(self.doc.currency, self.doc.conversion_rate, + self.meta.get_label("conversion_rate"), self.doc.company) + + self.calculate_taxes_and_totals() self.validate_value("grand_total", ">=", 0) + self.set_total_in_words() + + def set_price_list_currency(self, buying_or_selling): + # TODO - change this, since price list now has only one currency allowed + if self.meta.get_field("price_list_name") and self.doc.price_list_name and \ + not self.doc.price_list_currency: + self.doc.fields.update(get_price_list_currency({ + "price_list_name": self.doc.price_list_name, + "use_for": buying_or_selling + })) + + def set_missing_item_details(self, get_item_details): + """set missing item values""" + for item in self.doclist.get({"parentfield": self.fname}): + if item.fields.get("item_code"): + args = item.fields.copy().update(self.doc.fields) + ret = get_item_details(args) + for fieldname, value in ret.items(): + if self.meta.get_field(fieldname, parentfield=self.fname) and \ + not item.fields.get(fieldname): + item.fields[fieldname] = value + + def set_taxes(self, tax_doctype, tax_parentfield, tax_master_field): + if not self.meta.get_field(tax_parentfield): + return + + if not self.doclist.get({"parentfield": tax_parentfield}): + if not self.doc.fields.get(tax_master_field): + # get the default tax master + self.doc.fields[tax_master_field] = \ + webnotes.conn.get_value(tax_doctype + " Master", {"is_default": 1}) + + if self.doc.fields.get(tax_master_field): + from webnotes.model import default_fields + tax_master = webnotes.bean(tax_doctype, self.doc.fields.get(tax_master_field)) + + for i, tax in enumerate(tax_master.doclist.get({"parentfield": tax_parentfield})): + for fieldname in default_fields: + tax.fields[fieldname] = None + + tax.fields.update({ + "doctype": tax_doctype, + "parentfield": tax_parentfield, + "idx": i+1 + }) + + self.doclist.append(tax) + + def calculate_taxes_and_totals(self): + self.doc.conversion_rate = flt(self.doc.conversion_rate) + self.item_doclist = self.doclist.get({"parentfield": self.fname}) + self.tax_doclist = self.doclist.get({"parentfield": self.other_fname}) + + self.calculate_item_values() + self.initialize_taxes() + + if hasattr(self, "determine_exclusive_rate"): + self.determine_exclusive_rate() + + self.calculate_net_total() + self.calculate_taxes() + self.calculate_totals() + self._cleanup() + + # TODO + # print format: show net_total_export instead of net_total + + def initialize_taxes(self): + for tax in self.tax_doclist: + tax.item_wise_tax_detail = {} + for fieldname in ["tax_amount", "total", + "tax_amount_for_current_item", "grand_total_for_current_item", + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]: + tax.fields[fieldname] = 0.0 + + self.validate_on_previous_row(tax) + self.validate_inclusive_tax(tax) + self.round_floats_in(tax) + + def validate_on_previous_row(self, tax): + """ + validate if a valid row id is mentioned in case of + On Previous Row Amount and On Previous Row Total + """ + if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \ + (not tax.row_id or cint(tax.row_id) >= tax.idx): + msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \ + _("Please specify a valid") + " %(row_id_label)s") % { + "idx": tax.idx, + "taxes_doctype": tax.doctype, + "row_id_label": self.meta.get_label("row_id", + parentfield=self.other_fname) + }, raise_exception=True) + + def validate_inclusive_tax(self, tax): + def _on_previous_row_error(row_range): + msgprint((_("Row") + " # %(idx)s [%(doctype)s]: " + + _("to be included in Item's rate, it is required that: ") + + " [" + _("Row") + " # %(row_range)s] " + _("also be included in Item's rate")) % { + "idx": tax.idx, + "doctype": tax.doctype, + "inclusive_label": self.meta.get_label("included_in_print_rate", + parentfield=self.other_fname), + "charge_type_label": self.meta.get_label("charge_type", + parentfield=self.other_fname), + "charge_type": tax.charge_type, + "row_range": row_range + }, raise_exception=True) + + if cint(tax.included_in_print_rate): + if tax.charge_type == "Actual": + # inclusive tax cannot be of type Actual + msgprint((_("Row") + + " # %(idx)s [%(doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" " + + "cannot be included in Item's rate") % { + "idx": tax.idx, + "doctype": tax.doctype, + "charge_type_label": self.meta.get_label("charge_type", + parentfield=self.other_fname), + "charge_type": tax.charge_type, + }, raise_exception=True) + elif tax.charge_type == "On Previous Row Amount" and \ + not cint(self.tax_doclist[tax.row_id - 1].included_in_print_rate): + # referred row should also be inclusive + _on_previous_row_error(tax.row_id) + elif tax.charge_type == "On Previous Row Total" and \ + not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:tax.row_id - 1]]): + # all rows about the reffered tax should be inclusive + _on_previous_row_error("1 - %d" % (tax.row_id,)) + + def calculate_taxes(self): + for item in self.item_doclist: + item_tax_map = self._load_item_tax_rate(item.item_tax_rate) + + for i, tax in enumerate(self.tax_doclist): + # tax_amount represents the amount of tax for the current step + current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map) + + if hasattr(self, "set_item_tax_amount"): + self.set_item_tax_amount(item, tax, current_tax_amount) + + # case when net total is 0 but there is an actual type charge + # in this case add the actual amount to tax.tax_amount + # and tax.grand_total_for_current_item for the first such iteration + if tax.charge_type=="Actual" and \ + not (current_tax_amount or self.doc.net_total or tax.tax_amount): + zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax)) + current_tax_amount += zero_net_total_adjustment + + # store tax_amount for current item as it will be used for + # charge type = 'On Previous Row Amount' + tax.tax_amount_for_current_item = current_tax_amount + + # accumulate tax amount into tax.tax_amount + tax.tax_amount += current_tax_amount + + # store tax breakup for each item + tax.item_wise_tax_detail[item.item_code] = current_tax_amount + + if tax.category: + # if just for valuation, do not add the tax amount in total + # hence, setting it as 0 for further steps + current_tax_amount = 0.0 if (tax.category == "Valuation") else current_tax_amount + + current_tax_amount *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0 + + # Calculate tax.total viz. grand total till that step + # note: grand_total_for_current_item contains the contribution of + # item's amount, previously applied tax and the current tax on that item + if i==0: + tax.grand_total_for_current_item = flt(item.amount + + current_tax_amount, self.precision("total", tax)) + + else: + tax.grand_total_for_current_item = \ + flt(self.tax_doclist[i-1].grand_total_for_current_item + + current_tax_amount, self.precision("total", tax)) + + # in tax.total, accumulate grand total of each item + tax.total += tax.grand_total_for_current_item + + def get_current_tax_amount(self, item, tax, item_tax_map): + tax_rate = self._get_tax_rate(tax, item_tax_map) + current_tax_amount = 0.0 + + if tax.charge_type == "Actual": + # distribute the tax amount proportionally to each item row + actual = flt(tax.rate, self.precision("tax_amount", tax)) + current_tax_amount = (self.doc.net_total + and ((item.amount / self.doc.net_total) * actual) + or 0) + elif tax.charge_type == "On Net Total": + current_tax_amount = (tax_rate / 100.0) * item.amount + elif tax.charge_type == "On Previous Row Amount": + current_tax_amount = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item + elif tax.charge_type == "On Previous Row Total": + current_tax_amount = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item + + return flt(current_tax_amount, self.precision("tax_amount", tax)) + + def _load_item_tax_rate(self, item_tax_rate): + return json.loads(item_tax_rate) if item_tax_rate else {} + + def _get_tax_rate(self, tax, item_tax_map): + if item_tax_map.has_key(tax.account_head): + return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax)) + else: + return tax.rate + + def _cleanup(self): + for tax in self.tax_doclist: + for fieldname in ("grand_total_for_current_item", + "tax_amount_for_current_item", + "tax_fraction_for_current_item", + "grand_total_fraction_for_current_item"): + if fieldname in tax.fields: + del tax.fields[fieldname] + + tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail) + + def _set_in_company_currency(self, item, print_field, base_field): + """set values in base currency""" + item.fields[base_field] = flt((flt(item.fields[print_field], + self.precision(print_field, item)) * self.doc.conversion_rate), + self.precision(base_field, item)) + + def calculate_total_advance(self, parenttype, advance_parentfield): + if self.doc.doctype == parenttype and self.doc.docstatus < 2: + sum_of_allocated_amount = sum([flt(adv.allocated_amount, self.precision("allocated_amount", adv)) + for adv in self.doclist.get({"parentfield": advance_parentfield})]) + + self.doc.total_advance = flt(sum_of_allocated_amount, self.precision("total_advance")) + + self.calculate_outstanding_amount() def get_gl_dict(self, args, cancel=None): """this method populates the common properties of a gl entry record""" @@ -97,4 +343,4 @@ class AccountsController(TransactionBase): if not hasattr(self, "_abbr"): self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr") - return self._abbr \ No newline at end of file + return self._abbr diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py index 3deda0284be..303eef3894f 100644 --- a/controllers/buying_controller.py +++ b/controllers/buying_controller.py @@ -17,35 +17,45 @@ from __future__ import unicode_literals import webnotes from webnotes import _, msgprint -from webnotes.utils import flt, cint -import json +from webnotes.utils import flt from buying.utils import get_item_details from setup.utils import get_company_currency -from webnotes.model.utils import round_floats_in_doc from controllers.stock_controller import StockController class WrongWarehouseCompany(Exception): pass class BuyingController(StockController): + def onload_post_render(self): + self.set_price_list_currency("buying") + + # contact, address, item details + self.set_missing_values() + + self.set_taxes("Purchase Taxes and Charges", "purchase_tax_details", "purchase_other_charges") + def validate(self): super(BuyingController, self).validate() self.validate_stock_or_nonstock_items() self.validate_warehouse_belongs_to_company() - if self.meta.get_field("currency"): - self.company_currency = get_company_currency(self.doc.company) - self.validate_conversion_rate("currency", "conversion_rate") - - if self.doc.price_list_name and self.doc.price_list_currency: - self.validate_conversion_rate("price_list_currency", "plc_conversion_rate") - - # IMPORTANT: enable this only when client side code is similar to this one - # self.calculate_taxes_and_totals() + + def set_missing_values(self, for_validate=False): + # set contact and address details for supplier, if they are not mentioned + if self.doc.supplier and not (self.doc.contact_person and self.doc.supplier_address): + for fieldname, val in self.get_default_address_and_contact("supplier").items(): + if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): + self.doc.fields[fieldname] = val + + self.set_missing_item_details(get_item_details) + + def set_supplier_defaults(self): + self.get_default_supplier_address(self.doc.fields) - # set total in words - self.set_total_in_words() - + def get_purchase_tax_details(self): + self.doclist = self.doc.clear_table(self.doclist, "purchase_tax_details") + self.set_taxes() + def validate_warehouse_belongs_to_company(self): for warehouse, company in webnotes.conn.get_values("Warehouse", self.doclist.get_distinct_values("warehouse"), "company").items(): @@ -71,46 +81,6 @@ class BuyingController(StockController): webnotes.msgprint(_("""Tax Category can not be 'Valuation' or 'Valuation and Total' as all items are non-stock items"""), raise_exception=1) - def update_item_details(self): - for item in self.doclist.get({"parentfield": self.fname}): - ret = get_item_details({ - "doctype": self.doc.doctype, - "docname": self.doc.name, - "item_code": item.item_code, - "warehouse": item.warehouse, - "supplier": self.doc.supplier, - "transaction_date": self.doc.posting_date, - "conversion_rate": self.doc.conversion_rate, - "price_list_name": self.doc.price_list_name, - "price_list_currency": self.doc.price_list_currency, - "plc_conversion_rate": self.doc.plc_conversion_rate - }) - for r in ret: - if not item.fields.get(r): - item.fields[r] = ret[r] - - def validate_conversion_rate(self, currency_field, conversion_rate_field): - """common validation for currency and price list currency""" - - currency = self.doc.fields.get(currency_field) - conversion_rate = flt(self.doc.fields.get(conversion_rate_field)) - conversion_rate_label = self.meta.get_label(conversion_rate_field) - - if conversion_rate == 0: - msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True) - - # parenthesis for 'OR' are necessary as we want it to evaluate as - # mandatory valid condition and (1st optional valid condition - # or 2nd optional valid condition) - valid_conversion_rate = (conversion_rate and - ((currency == self.company_currency and conversion_rate == 1.00) - or (currency != self.company_currency and conversion_rate != 1.00))) - - if not valid_conversion_rate: - msgprint(_('Please enter valid ') + conversion_rate_label + (': ') - + ("1 %s = [?] %s" % (currency, self.company_currency)), - raise_exception=True) - def set_total_in_words(self): from webnotes.utils import money_in_words company_currency = get_company_currency(self.doc.company) @@ -121,154 +91,54 @@ class BuyingController(StockController): self.doc.currency) def calculate_taxes_and_totals(self): - self.doc.conversion_rate = flt(self.doc.conversion_rate) - self.item_doclist = self.doclist.get({"parentfield": self.fname}) - self.tax_doclist = self.doclist.get({"parentfield": "purchase_tax_details"}) - - self.calculate_item_values() - self.initialize_taxes() - self.calculate_net_total() - self.calculate_taxes() - self.calculate_totals() - self.calculate_outstanding_amount() - - self._cleanup() + self.other_fname = "purchase_tax_details" + super(BuyingController, self).calculate_taxes_and_totals() + self.calculate_total_advance("Purchase Invoice", "advance_allocation_details") def calculate_item_values(self): - def _set_base(item, print_field, base_field): - """set values in base currency""" - item.fields[base_field] = flt((flt(item.fields[print_field], - self.precision.item[print_field]) * self.doc.conversion_rate), - self.precision.item[base_field]) - - for item in self.item_doclist: - round_floats_in_doc(item, self.precision.item) + # hack! - cleaned up in _cleanup() + if self.doc.doctype != "Purchase Invoice": + df = self.meta.get_field("purchase_rate", parentfield=self.fname) + df.fieldname = "rate" + for item in self.item_doclist: # hack! - cleaned up in _cleanup() if self.doc.doctype != "Purchase Invoice": item.rate = item.purchase_rate - self.precision.item.rate = self.precision.item.purchase_rate - item.discount = item.discount_rate - self.precision.item.discount = self.precision.item.discount_rate + self.round_floats_in(item) - if item.discount == 100: - if not item.import_ref_rate: - item.import_ref_rate = item.import_rate - item.import_rate = 0 - else: - if item.import_ref_rate: - item.import_rate = flt(item.import_ref_rate * - (1.0 - (item.discount_rate / 100.0)), - self.precision.item.import_rate) - else: - # assume that print rate and discount are specified - item.import_ref_rate = flt(item.import_rate / - (1.0 - (item.discount_rate / 100.0)), - self.precision.item.import_ref_rate) + if item.discount_rate == 100.0: + item.import_rate = 0.0 + elif item.import_ref_rate: + item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)), + self.precision("import_rate", item)) item.import_amount = flt(item.import_rate * item.qty, - self.precision.item.import_amount) + self.precision("import_amount", item)) + item.item_tax_amount = 0.0; - _set_base(item, "import_ref_rate", "purchase_ref_rate") - _set_base(item, "import_rate", "rate") - _set_base(item, "import_amount", "amount") - - def initialize_taxes(self): - for tax in self.tax_doclist: - # initialize totals to 0 - tax.tax_amount = tax.total = 0.0 + self._set_in_company_currency(item, "import_ref_rate", "purchase_ref_rate") + self._set_in_company_currency(item, "import_rate", "rate") + self._set_in_company_currency(item, "import_amount", "amount") - # temporary fields - tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0 - - tax.item_wise_tax_detail = {} - - self.validate_on_previous_row(tax) - - round_floats_in_doc(tax, self.precision.tax) - def calculate_net_total(self): - self.doc.net_total = 0 - self.doc.net_total_import = 0 + self.doc.net_total = self.doc.net_total_import = 0.0 for item in self.item_doclist: self.doc.net_total += item.amount self.doc.net_total_import += item.import_amount - self.doc.net_total = flt(self.doc.net_total, self.precision.main.net_total) - self.doc.net_total_import = flt(self.doc.net_total_import, - self.precision.main.net_total_import) - - def calculate_taxes(self): - for item in self.item_doclist: - item_tax_map = self._load_item_tax_rate(item.item_tax_rate) - item.item_tax_amount = 0 - - for i, tax in enumerate(self.tax_doclist): - # tax_amount represents the amount of tax for the current step - current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map) - - self.set_item_tax_amount(item, tax, current_tax_amount) - - # case when net total is 0 but there is an actual type charge - # in this case add the actual amount to tax.tax_amount - # and tax.grand_total_for_current_item for the first such iteration - if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \ - tax.charge_type=="Actual": - zero_net_total_adjustment = flt(tax.rate, self.precision.tax.tax_amount) - current_tax_amount += zero_net_total_adjustment - - # store tax_amount for current item as it will be used for - # charge type = 'On Previous Row Amount' - tax.tax_amount_for_current_item = current_tax_amount - - # accumulate tax amount into tax.tax_amount - tax.tax_amount += tax.tax_amount_for_current_item - - if tax.category == "Valuation": - # if just for valuation, do not add the tax amount in total - # hence, setting it as 0 for further steps - current_tax_amount = 0 - else: - current_tax_amount *= tax.add_deduct_tax == "Deduct" and -1.0 or 1.0 - - # Calculate tax.total viz. grand total till that step - # note: grand_total_for_current_item contains the contribution of - # item's amount, previously applied tax and the current tax on that item - if i==0: - tax.grand_total_for_current_item = flt(item.amount + - current_tax_amount, self.precision.tax.total) - - else: - tax.grand_total_for_current_item = \ - flt(self.tax_doclist[i-1].grand_total_for_current_item + - current_tax_amount, self.precision.tax.total) - - # in tax.total, accumulate grand total of each item - tax.total += tax.grand_total_for_current_item - - # store tax_breakup for each item - # DOUBT: should valuation type amount also be stored? - tax.item_wise_tax_detail[item.item_code] = current_tax_amount + self.round_floats_in(self.doc, ["net_total", "net_total_import"]) def calculate_totals(self): - if self.tax_doclist: - self.doc.grand_total = flt(self.tax_doclist[-1].total, - self.precision.main.grand_total) - self.doc.grand_total_import = flt( - self.doc.grand_total / self.doc.conversion_rate, - self.precision.main.grand_total_import) - else: - self.doc.grand_total = flt(self.doc.net_total, - self.precision.main.grand_total) - self.doc.grand_total_import = flt( - self.doc.grand_total / self.doc.conversion_rate, - self.precision.main.grand_total_import) + self.doc.grand_total = flt(self.tax_doclist and \ + self.tax_doclist[-1].total or self.doc.net_total, self.precision("grand_total")) + self.doc.grand_total_import = flt(self.doc.grand_total / self.doc.conversion_rate, + self.precision("grand_total_import")) - self.doc.total_tax = \ - flt(self.doc.grand_total - self.doc.net_total, - self.precision.main.total_tax) + self.doc.total_tax = flt(self.doc.grand_total - self.doc.net_total, + self.precision("total_tax")) if self.meta.get_field("rounded_total"): self.doc.rounded_total = round(self.doc.grand_total) @@ -277,75 +147,31 @@ class BuyingController(StockController): self.doc.rounded_total_import = round(self.doc.grand_total_import) def calculate_outstanding_amount(self): - if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus == 0: + if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus < 2: self.doc.total_advance = flt(self.doc.total_advance, - self.precision.main.total_advance) + self.precision("total_advance")) self.doc.total_amount_to_pay = flt(self.doc.grand_total - flt(self.doc.write_off_amount, - self.precision.main.write_off_amount), self.precision.main.total_amount_to_pay) + self.precision("write_off_amount")), self.precision("total_amount_to_pay")) self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance, - self.precision.main.outstanding_amount) + self.precision("outstanding_amount")) def _cleanup(self): - for tax in self.tax_doclist: - del tax.fields["grand_total_for_current_item"] - del tax.fields["tax_amount_for_current_item"] - tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail) - - # except in purchase invoice, rate field is purchase_rate + super(BuyingController, self)._cleanup() + + # except in purchase invoice, rate field is purchase_rate + # reset fieldname of rate if self.doc.doctype != "Purchase Invoice": + df = self.meta.get_field("rate", parentfield=self.fname) + df.fieldname = "purchase_rate" + for item in self.item_doclist: item.purchase_rate = item.rate del item.fields["rate"] - item.discount_rate = item.discount - del item.fields["discount"] - - def validate_on_previous_row(self, tax): - """ - validate if a valid row id is mentioned in case of - On Previous Row Amount and On Previous Row Total - """ - if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \ - (not tax.row_id or cint(tax.row_id) >= tax.idx): - msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \ - _("Please specify a valid") + " %(row_id_label)s") % { - "idx": tax.idx, - "taxes_doctype": tax.parenttype, - "row_id_label": self.meta.get_label("row_id", - parentfield="purchase_tax_details") - }, raise_exception=True) + if not self.meta.get_field("item_tax_amount", parentfield=self.fname): + for item in self.item_doclist: + del item.fields["item_tax_amount"] - def _load_item_tax_rate(self, item_tax_rate): - if not item_tax_rate: - return {} - return json.loads(item_tax_rate) - - def get_current_tax_amount(self, item, tax, item_tax_map): - tax_rate = self._get_tax_rate(tax, item_tax_map) - - if tax.charge_type == "Actual": - # distribute the tax amount proportionally to each item row - actual = flt(tax.rate, self.precision.tax.tax_amount) - current_tax_amount = (self.doc.net_total - and ((item.amount / self.doc.net_total) * actual) - or 0) - elif tax.charge_type == "On Net Total": - current_tax_amount = (tax_rate / 100.0) * item.amount - elif tax.charge_type == "On Previous Row Amount": - current_tax_amount = (tax_rate / 100.0) * \ - self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item - elif tax.charge_type == "On Previous Row Total": - current_tax_amount = (tax_rate / 100.0) * \ - self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item - - return flt(current_tax_amount, self.precision.tax.tax_amount) - - def _get_tax_rate(self, tax, item_tax_map): - if item_tax_map.has_key(tax.account_head): - return flt(item_tax_map.get(tax.account_head), self.precision.tax.rate) - else: - return tax.rate - def set_item_tax_amount(self, item, tax, current_tax_amount): """ item_tax_amount is the total tax amount applied on that item @@ -353,29 +179,28 @@ class BuyingController(StockController): TODO: rename item_tax_amount to valuation_tax_amount """ - if tax.category in ["Valuation", "Valuation and Total"] and \ - item.item_code in self.stock_items: - item.item_tax_amount += flt(current_tax_amount, - self.precision.item.item_tax_amount) + if tax.category in ["Valuation", "Valuation and Total"]: + item.item_tax_amount += flt(current_tax_amount, self.precision("item_tax_amount", item)) # update valuation rate def update_valuation_rate(self, parentfield): - for d in self.doclist.get({"parentfield": parentfield}): - d.conversion_factor = d.conversion_factor or flt(webnotes.conn.get_value( - "UOM Conversion Detail", {"parent": d.item_code, "uom": d.uom}, + for item in self.doclist.get({"parentfield": parentfield}): + item.conversion_factor = item.conversion_factor or flt(webnotes.conn.get_value( + "UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom}, "conversion_factor")) or 1 - if d.item_code and d.qty: + + if item.item_code and item.qty: + self.round_floats_in(item) + + purchase_rate = item.rate if self.doc.doctype == "Purchase Invoice" else item.purchase_rate + # if no item code, which is sometimes the case in purchase invoice, # then it is not possible to track valuation against it - d.valuation_rate = flt(((flt(d.purchase_rate, self.precision.item.purchase_rate) or - flt(d.rate, self.precision.item.rate)) + - (flt(d.item_tax_amount, self.precision.item.item_tax_amount) + - flt(d.rm_supp_cost, self.precision.item.rm_supp_cost)) / - flt(d.qty, self.precision.item.qty)) / - flt(d.conversion_factor, self.precision.item.conversion_factor), - self.precision.item.valuation_rate) + item.valuation_rate = flt((purchase_rate + + (item.item_tax_amount + item.rm_supp_cost) / item.qty) / item.conversion_factor, + self.precision("valuation_rate", item)) else: - d.valuation_rate = 0.0 + item.valuation_rate = 0.0 def validate_for_subcontracting(self): if not self.doc.is_subcontracted and self.sub_contracted_items: @@ -437,18 +262,6 @@ class BuyingController(StockController): return bom_items - - @property - def precision(self): - if not hasattr(self, "_precision"): - self._precision = webnotes._dict() - self._precision.main = self.meta.get_precision_map() - self._precision.item = self.meta.get_precision_map(parentfield = self.fname) - if self.meta.get_field("purchase_tax_details"): - self._precision.tax = self.meta.get_precision_map(parentfield = \ - "purchase_tax_details") - return self._precision - @property def sub_contracted_items(self): if not hasattr(self, "_sub_contracted_items"): diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 80af3370315..3adc6392716 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -16,16 +16,40 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint +from webnotes.utils import cint, flt, comma_or from setup.utils import get_company_currency +from selling.utils import get_item_details from webnotes import msgprint, _ from controllers.stock_controller import StockController class SellingController(StockController): - def validate(self): - super(SellingController, self).validate() - self.set_total_in_words() + def onload_post_render(self): + self.set_price_list_currency("selling") + + # contact, address, item details and pos details (if applicable) + self.set_missing_values() + + self.set_taxes("Sales Taxes and Charges", "other_charges", "charge") + + def set_missing_values(self, for_validate=False): + # set contact and address details for customer, if they are not mentioned + if self.doc.customer and not (self.doc.contact_person and self.doc.customer_address): + for fieldname, val in self.get_default_address_and_contact("customer").items(): + if not self.doc.fields.get(fieldname) and self.meta.get_field(fieldname): + self.doc.fields[fieldname] = val + + self.set_missing_item_details(get_item_details) + + def get_other_charges(self): + self.doclist = self.doc.clear_table(self.doclist, "other_charges") + self.set_taxes("Sales Taxes and Charges", "other_charges", "charge") + + def set_customer_defaults(self): + self.get_default_customer_address() + + if self.meta.get_field("shipping_address"): + self.doc.fields.update(self.get_shipping_address(self.doc.customer)) def set_total_in_words(self): from webnotes.utils import money_in_words @@ -71,4 +95,154 @@ class SellingController(StockController): if item.buying_amount and not item.cost_center: msgprint(_("""Cost Center is mandatory for item: """) + item.item_code, - raise_exception=1) \ No newline at end of file + raise_exception=1) + + def calculate_taxes_and_totals(self): + self.other_fname = "other_charges" + + super(SellingController, self).calculate_taxes_and_totals() + + self.calculate_total_advance("Sales Invoice", "advance_adjustment_details") + self.calculate_commission() + self.calculate_contribution() + + def determine_exclusive_rate(self): + if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)): + # no inclusive tax + return + + for item in self.item_doclist: + item_tax_map = self._load_item_tax_rate(item.item_tax_rate) + cumulated_tax_fraction = 0 + for i, tax in enumerate(self.tax_doclist): + tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax, item_tax_map) + + if i==0: + tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item + else: + tax.grand_total_fraction_for_current_item = \ + self.tax_doclist[i-1].grand_total_fraction_for_current_item \ + + tax.tax_fraction_for_current_item + + cumulated_tax_fraction += tax.tax_fraction_for_current_item + + if cumulated_tax_fraction: + item.basic_rate = flt((item.export_rate * self.doc.conversion_rate) / + (1 + cumulated_tax_fraction), self.precision("basic_rate", item)) + + item.amount = flt(item.basic_rate * item.qty, self.precision("amount", item)) + + if item.adj_rate == 100: + item.base_ref_rate = item.basic_rate + item.basic_rate = 0.0 + else: + item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)), + self.precision("base_ref_rate", item)) + + def get_current_tax_fraction(self, tax, item_tax_map): + """ + Get tax fraction for calculating tax exclusive amount + from tax inclusive amount + """ + current_tax_fraction = 0 + + if cint(tax.included_in_print_rate): + tax_rate = self._get_tax_rate(tax, item_tax_map) + + if tax.charge_type == "On Net Total": + current_tax_fraction = tax_rate / 100.0 + + elif tax.charge_type == "On Previous Row Amount": + current_tax_fraction = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item + + elif tax.charge_type == "On Previous Row Total": + current_tax_fraction = (tax_rate / 100.0) * \ + self.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item + + return current_tax_fraction + + def calculate_item_values(self): + for item in self.item_doclist: + self.round_floats_in(item) + + if item.adj_rate == 100: + item.export_rate = 0 + elif item.ref_rate: + item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)), + self.precision("export_rate", item)) + + item.export_amount = flt(item.export_rate * item.qty, + self.precision("export_amount", item)) + + self._set_in_company_currency(item, "ref_rate", "base_ref_rate") + self._set_in_company_currency(item, "export_rate", "basic_rate") + self._set_in_company_currency(item, "export_amount", "amount") + + def calculate_net_total(self): + self.doc.net_total = self.doc.net_total_export = 0.0 + + for item in self.item_doclist: + self.doc.net_total += item.amount + self.doc.net_total_export += item.export_amount + + self.round_floats_in(self.doc, ["net_total", "net_total_export"]) + + def calculate_totals(self): + self.doc.grand_total = flt(self.tax_doclist and \ + self.tax_doclist[-1].total or self.doc.net_total, self.precision("grand_total")) + self.doc.grand_total_export = flt(self.doc.grand_total / self.doc.conversion_rate, + self.precision("grand_total_export")) + + self.doc.other_charges_total = flt(self.doc.grand_total - self.doc.net_total, + self.precision("other_charges_total")) + self.doc.other_charges_total_export = flt(self.doc.grand_total_export - self.doc.net_total_export, + self.precision("other_charges_total_export")) + + self.doc.rounded_total = round(self.doc.grand_total) + self.doc.rounded_total_export = round(self.doc.grand_total_export) + + def calculate_outstanding_amount(self): + # NOTE: + # write_off_amount is only for POS Invoice + # total_advance is only for non POS Invoice + if self.doc.doctype == "Sales Invoice" and self.doc.docstatus < 2: + self.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount", + "paid_amount"]) + total_amount_to_pay = self.doc.grand_total - self.doc.write_off_amount + self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount, + self.precision("outstanding_amount")) + + def calculate_commission(self): + if self.meta.get_field("commission_rate"): + self.round_floats_in(self.doc, ["net_total", "commission_rate"]) + if self.doc.commission_rate > 100.0: + msgprint(_(self.meta.get_label("commission_rate")) + " " + + _("cannot be greater than 100"), raise_exception=True) + + self.doc.total_commission = flt(self.doc.net_total * self.doc.commission_rate / 100.0, + self.precision("total_commission")) + + def calculate_contribution(self): + total = 0.0 + sales_team = self.doclist.get({"parentfield": "sales_team"}) + for sales_person in sales_team: + self.round_floats_in(sales_person) + + sales_person.allocated_amount = flt( + self.doc.net_total * sales_person.allocated_percentage / 100.0, + self.precision("allocated_amount", sales_person)) + + total += sales_person.allocated_percentage + + if sales_team and total != 100.0: + msgprint(_("Total") + " " + + _(self.meta.get_label("allocated_percentage", parentfield="sales_team")) + + " " + _("should be 100%"), raise_exception=True) + + def validate_order_type(self): + valid_types = ["Sales", "Maintenance"] + if self.doc.order_type not in valid_types: + msgprint(_(self.meta.get_label("order_type")) + " " + + _("must be one of") + ": " + comma_or(valid_types), + raise_exception=True) diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py index 39449a62526..0168de63f15 100644 --- a/patches/april_2013/p05_update_file_data.py +++ b/patches/april_2013/p05_update_file_data.py @@ -1,5 +1,4 @@ import webnotes, webnotes.utils, os -from webnotes.modules.export_file import export_to_files def execute(): webnotes.reload_doc("core", "doctype", "file_data") @@ -71,4 +70,4 @@ def update_for_doc(doctype, doc): pass else: webnotes.conn.sql("""delete from `tabFile Data` where name=%s""", - fileid) \ No newline at end of file + fileid) diff --git a/patches/april_2013/p06_default_cost_center.py b/patches/april_2013/p06_default_cost_center.py index 4f80d95d0e5..4aaa7d53b3e 100644 --- a/patches/april_2013/p06_default_cost_center.py +++ b/patches/april_2013/p06_default_cost_center.py @@ -1,10 +1,10 @@ import webnotes def execute(): - for dt, fieldname in \ - (("Journal Voucher Detail", "cost_center"), - ("Sales Taxes and Charges", "cost_center_other_charges"), - ("Purchase Taxes and Charges", "cost_center"), ("Delivery Note Item", "cost_center"), - ("Purchase Invoice Item", "cost_center"), ("Sales Invoice Item", "cost_center")): - webnotes.conn.sql_ddl("""alter table `tab%s` alter `%s` drop default""" % (dt, fieldname)) + webnotes.reload_doc("Stock", "DocType", "Delivery Note Item") + for dt in ("Journal Voucher Detail", "Sales Taxes and Charges", + "Purchase Taxes and Charges", "Delivery Note Item", + "Purchase Invoice Item", "Sales Invoice Item"): + webnotes.conn.sql_ddl("""alter table `tab%s` alter `cost_center` drop default""" \ + % (dt,)) webnotes.reload_doc(webnotes.conn.get_value("DocType", dt, "module"), "DocType", dt) diff --git a/patches/april_2013/p07_rename_cost_center_other_charges.py b/patches/april_2013/p07_rename_cost_center_other_charges.py new file mode 100644 index 00000000000..c3c94912cf4 --- /dev/null +++ b/patches/april_2013/p07_rename_cost_center_other_charges.py @@ -0,0 +1,9 @@ +import webnotes + +def execute(): + webnotes.reload_doc("Accounts", "DocType", "Sales Taxes and Charges") + webnotes.conn.sql("""update `tabSales Taxes and Charges` + set cost_center = cost_center_other_charges""") + webnotes.conn.sql_ddl("""alter table `tabSales Taxes and Charges` + drop column cost_center_other_charges""") + \ No newline at end of file diff --git a/patches/may_2013/p01_selling_net_total_export.py b/patches/may_2013/p01_selling_net_total_export.py new file mode 100644 index 00000000000..eb6da234ab7 --- /dev/null +++ b/patches/may_2013/p01_selling_net_total_export.py @@ -0,0 +1,16 @@ +from __future__ import unicode_literals +import webnotes +from webnotes.utils import cint + +def execute(): + for module, doctype in (("Accounts", "Sales Invoice"), ("Selling", "Sales Order"), ("Selling", "Quotation"), + ("Stock", "Delivery Note")): + webnotes.reload_doc(module, "DocType", doctype) + webnotes.conn.sql("""update `tab%s` + set net_total_export = round(net_total / if(conversion_rate=0, 1, ifnull(conversion_rate, 1)), 2), + other_charges_total_export = round(grand_total_export - net_total_export, 2)""" % + (doctype,)) + + for module, doctype in (("Accounts", "Sales Invoice Item"), ("Selling", "Sales Order Item"), ("Selling", "Quotation Item"), + ("Stock", "Delivery Note Item")): + webnotes.reload_doc(module, "DocType", doctype) \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index e02d8e0c835..300a8afdeea 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -240,12 +240,13 @@ patch_list = [ "patches.april_2013.p06_update_file_size", "patches.april_2013.p05_fixes_in_reverse_modules", "execute:webnotes.delete_doc('DocType Mapper', 'Delivery Note-Packing Slip')", - "execute:webnotes.reload_doc('Stock', 'DocType', 'Delivery Note Item')", + "patches.april_2013.p07_rename_cost_center_other_charges", "patches.april_2013.p06_default_cost_center", "execute:webnotes.reset_perms('File Data')", "patches.april_2013.p07_update_file_data_2", "patches.april_2013.rebuild_sales_browser", "patches.april_2013.p08_price_list_country", + "patches.may_2013.p01_selling_net_total_export", "patches.may_2013.repost_stock_for_no_posting_time", "patches.may_2013.p01_conversion_factor_and_aii", "patches.may_2013.p02_update_valuation_rate", diff --git a/public/js/transaction.js b/public/js/transaction.js new file mode 100644 index 00000000000..78f792d0185 --- /dev/null +++ b/public/js/transaction.js @@ -0,0 +1,498 @@ +// ERPNext - web based ERP (http://erpnext.com) +// Copyright (C) 2012 Web Notes Technologies Pvt Ltd +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +wn.provide("erpnext"); + +erpnext.TransactionController = wn.ui.form.Controller.extend({ + onload: function() { + if(this.frm.doc.__islocal) { + var me = this, + today = get_today(), + currency = wn.defaults.get_default("currency"); + + $.each({ + posting_date: today, + due_date: today, + transaction_date: today, + currency: currency, + price_list_currency: currency, + status: "Draft", + company: wn.defaults.get_default("company"), + fiscal_year: wn.defaults.get_default("fiscal_year"), + is_subcontracted: "No", + conversion_rate: 1.0, + plc_conversion_rate: 1.0 + }, function(fieldname, value) { + if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) + me.frm.set_value(fieldname, value); + }); + } + }, + + refresh: function() { + this.frm.clear_custom_buttons(); + erpnext.hide_naming_series(); + this.show_item_wise_taxes(); + this.frm.fields_dict.currency ? this.currency() : this.set_dynamic_labels(); + }, + + onload_post_render: function() { + if(this.frm.doc.__islocal && this.frm.doc.company) { + var me = this; + this.frm.call({ + doc: this.frm.doc, + method: "onload_post_render", + freeze: true, + callback: function(r) { + // remove this call when using client side mapper + me.set_default_values(); + me.frm.refresh(); + } + }); + } + }, + + validate: function() { + this.calculate_taxes_and_totals(); + }, + + company: function() { + if(this.frm.doc.company) { + var me = this; + var company_currency = this.get_company_currency(); + $.each(["currency", "price_list_currency"], function(i, fieldname) { + if(!me.doc[fieldname]) { + me.frm.set_value(fieldname, company_currency); + } + }); + this.price_list_currency(); + } + }, + + get_company_currency: function() { + return erpnext.get_currency(this.frm.doc.company); + }, + + currency: function() { + this.price_list_currency(); + }, + + price_list_name: function(use_for) { + var me = this; + if(this.frm.doc.price_list_name) { + this.frm.call({ + method: "setup.utils.get_price_list_currency", + args: {args: { + price_list_name: this.frm.doc.price_list_name, + use_for: use_for + }}, + callback: function(r) { + if(!r.exc) { + me.price_list_currency(); + } + } + }); + } + }, + + price_list_currency: function() { + // What TODO? should we make price list system non-mandatory? + this.frm.toggle_reqd("plc_conversion_rate", + !!(this.frm.doc.price_list_name && this.frm.doc.price_list_currency)); + + if(this.frm.doc.price_list_currency === this.get_company_currency()) { + this.frm.set_value("plc_conversion_rate", 1.0); + } else if(this.frm.doc.price_list_currency === this.frm.doc.currency) { + this.frm.set_value("plc_conversion_rate", this.frm.doc.conversion_rate); + } + this.set_dynamic_labels(); + }, + + plc_conversion_rate: function() { + this.price_list_currency(); + }, + + conversion_rate: function() { + this.price_list_currency(); + this.calculate_taxes_and_totals(); + }, + + qty: function(doc, cdt, cdn) { + this.calculate_taxes_and_totals(); + }, + + tax_rate: function(doc, cdt, cdn) { + this.calculate_taxes_and_totals(); + }, + + row_id: function(doc, cdt, cdn) { + var tax = wn.model.get_doc(cdt, cdn); + try { + this.validate_on_previous_row(tax); + this.calculate_taxes_and_totals(); + } catch(e) { + tax.row_id = null; + refresh_field("row_id", tax.name, tax.parentfield); + throw e; + } + }, + + recalculate: function() { + this.calculate_taxes_and_totals(); + }, + + recalculate_values: function() { + this.calculate_taxes_and_totals(); + }, + + calculate_charges: function() { + this.calculate_taxes_and_totals(); + }, + + included_in_print_rate: function(doc, cdt, cdn) { + var tax = wn.model.get_doc(cdt, cdn); + try { + this.validate_on_previous_row(tax); + this.validate_inclusive_tax(tax); + this.calculate_taxes_and_totals(); + } catch(e) { + tax.included_in_print_rate = 0; + refresh_field("included_in_print_rate", tax.name, tax.parentfield); + throw e; + } + }, + + validate_on_previous_row: function(tax) { + // validate if a valid row id is mentioned in case of + // On Previous Row Amount and On Previous Row Total + if((["On Previous Row Amount", "On Previous Row Total"].indexOf(tax.charge_type) != -1) && + (!tax.row_id || cint(tax.row_id) >= tax.idx)) { + var msg = repl(wn._("Row") + " # %(idx)s [%(doctype)s]: " + + wn._("Please specify a valid") + " %(row_id_label)s", { + idx: tax.idx, + doctype: tax.doctype, + row_id_label: wn.meta.get_label(tax.doctype, "row_id", tax.name) + }); + msgprint(msg); + throw msg; + } + }, + + validate_inclusive_tax: function(tax) { + if(!this.frm.tax_doclist) this.frm.tax_doclist = this.get_tax_doclist(); + + var actual_type_error = function() { + var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " + + "%(charge_type_label)s = \"%(charge_type)s\" " + + wn._("cannot be included in Item's rate"), { + idx: tax.idx, + doctype: tax.doctype, + charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name), + charge_type: tax.charge_type + }); + msgprint(msg); + throw msg; + }; + + var on_previous_row_error = function(row_range) { + var msg = repl(wn._("For row") + " # %(idx)s [%(doctype)s]: " + + wn._("to be included in Item's rate, it is required that: ") + + " [" + wn._("Row") + " # %(row_range)s] " + wn._("also be included in Item's rate"), { + idx: tax.idx, + doctype: tax.doctype, + charge_type_label: wn.meta.get_label(tax.doctype, "charge_type", tax.name), + charge_type: tax.charge_type, + inclusive_label: wn.meta.get_label(tax.doctype, "included_in_print_rate", tax.name), + row_range: row_range, + }); + + msgprint(msg); + throw msg; + }; + + if(cint(tax.included_in_print_rate)) { + if(tax.charge_type == "Actual") { + // inclusive tax cannot be of type Actual + actual_type_error(); + } else if(tax.charge_type == "On Previous Row Amount" && + !cint(this.frm.tax_doclist[tax.row_id - 1].included_in_print_rate)) { + // referred row should also be an inclusive tax + on_previous_row_error(tax.row_id); + } else if(tax.charge_type == "On Previous Row Total") { + var taxes_not_included = $.map(this.frm.tax_doclist.slice(0, tax.row_id), + function(t) { return cint(t.included_in_print_rate) ? null : t; }); + if(taxes_not_included.length > 0) { + // all rows above this tax should be inclusive + on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id); + } + } + } + }, + + _load_item_tax_rate: function(item_tax_rate) { + return item_tax_rate ? JSON.parse(item_tax_rate) : {}; + }, + + _get_tax_rate: function(tax, item_tax_map) { + return (keys(item_tax_map).indexOf(tax.account_head) != -1) ? + flt(item_tax_map[tax.account_head], precision("rate", tax)) : + tax.rate; + }, + + get_item_wise_taxes_html: function() { + var item_tax = {}; + var tax_accounts = []; + var company_currency = this.get_company_currency(); + + $.each(this.get_tax_doclist(), function(i, tax) { + var tax_amount_precision = precision("tax_amount", tax); + $.each(JSON.parse(tax.item_wise_tax_detail || '{}'), + function(item_code, tax_amount) { + if(!item_tax[item_code]) item_tax[item_code] = {}; + item_tax[item_code][tax.account_head] = flt(tax_amount, tax_amount_precision); + }); + tax_accounts.push(tax.account_head); + }); + + var headings = $.map([wn._("Item Name")].concat(tax_accounts), + function(head) { return '' + (head || "") + "" }).join("\n"); + + var rows = $.map(this.get_item_doclist(), function(item) { + var item_tax_record = item_tax[item.item_code || item.item_name]; + return repl("%(item_name)s%(taxes)s", { + item_name: item.item_name, + taxes: $.map(tax_accounts, function(head) { + return "" + format_currency(item_tax_record[head], company_currency) + "" + }).join("\n") + }); + }).join("\n"); + + return '
\ + ' + headings + ' \ + ' + rows + ' \ +
'; + }, + + set_default_values: function() { + $.each(wn.model.get_doclist(this.frm.doctype, this.frm.docname), function(i, doc) { + var updated = wn.model.set_default_values(doc); + if(doc.parentfield) { + refresh_field(doc.parentfield); + } else { + refresh_field(updated); + } + }); + }, + + _validate_before_fetch: function(fieldname) { + var me = this; + if(!me.frm.doc[fieldname]) { + return (wn._("Please specify") + ": " + + wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + ". " + wn._("It is needed to fetch Item Details.")); + } + return null; + }, + + validate_company_and_party: function(party_field) { + var me = this; + var valid = true; + var msg = ""; + $.each(["company", party_field], function(i, fieldname) { + var msg_for_fieldname = me._validate_before_fetch(fieldname); + if(msg_for_fieldname) { + msgprint(msg_for_fieldname); + valid = false; + } + }); + return valid; + }, + + get_item_doclist: function() { + return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: this.fname}); + }, + + get_tax_doclist: function() { + return wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: this.other_fname}); + }, + + validate_conversion_rate: function() { + this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); + var conversion_rate_label = wn.meta.get_label(this.frm.doc.doctype, "conversion_rate", + this.frm.doc.name); + + if(this.frm.doc.conversion_rate == 0) { + wn.throw(wn._(conversion_rate_label) + " " + wn._("cannot be 0")); + } + + var company_currency = this.get_company_currency(); + var valid_conversion_rate = this.frm.doc.conversion_rate ? + ((this.frm.doc.currency == company_currency && this.frm.doc.conversion_rate == 1.0) || + (this.frm.doc.currency != company_currency && this.frm.doc.conversion_rate != 1.0)) : + false; + + if(!valid_conversion_rate) { + wn.throw(wn._("Please enter valid") + " " + wn._(conversion_rate_label) + + " 1 " + this.frm.doc.currency + " = [?] " + company_currency); + } + }, + + calculate_taxes_and_totals: function() { + this.validate_conversion_rate(); + this.frm.item_doclist = this.get_item_doclist(); + this.frm.tax_doclist = this.get_tax_doclist(); + + this.calculate_item_values(); + this.initialize_taxes(); + this.determine_exclusive_rate && this.determine_exclusive_rate(); + this.calculate_net_total(); + this.calculate_taxes(); + this.calculate_totals(); + this._cleanup(); + + this.show_item_wise_taxes(); + }, + + initialize_taxes: function() { + var me = this; + $.each(this.frm.tax_doclist, function(i, tax) { + tax.item_wise_tax_detail = {}; + $.each(["tax_amount", "total", + "tax_amount_for_current_item", "grand_total_for_current_item", + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"], + function(i, fieldname) { tax[fieldname] = 0.0 }); + + me.validate_on_previous_row(tax); + me.validate_inclusive_tax(tax); + wn.model.round_floats_in(tax); + }); + }, + + calculate_taxes: function() { + var me = this; + + $.each(this.frm.item_doclist, function(n, item) { + var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); + + $.each(me.frm.tax_doclist, function(i, tax) { + // tax_amount represents the amount of tax for the current step + var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map); + + me.set_item_tax_amount && me.set_item_tax_amount(item, tax, current_tax_amount); + + // case when net total is 0 but there is an actual type charge + // in this case add the actual amount to tax.tax_amount + // and tax.grand_total_for_current_item for the first such iteration + if(tax.charge_type == "Actual" && + !(current_tax_amount || me.frm.doc.net_total || tax.tax_amount)) { + var zero_net_total_adjustment = flt(tax.rate, precision("tax_amount", tax)); + current_tax_amount += zero_net_total_adjustment; + } + + // store tax_amount for current item as it will be used for + // charge type = 'On Previous Row Amount' + tax.tax_amount_for_current_item = current_tax_amount; + + // accumulate tax amount into tax.tax_amount + tax.tax_amount += current_tax_amount; + + // store tax breakup for each item + tax.item_wise_tax_detail[item.item_code || item.item_name] = current_tax_amount; + + // for buying + if(tax.category) { + // if just for valuation, do not add the tax amount in total + // hence, setting it as 0 for further steps + current_tax_amount = (tax.category == "Valuation") ? 0.0 : current_tax_amount; + + current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; + } + + // Calculate tax.total viz. grand total till that step + // note: grand_total_for_current_item contains the contribution of + // item's amount, previously applied tax and the current tax on that item + if(i==0) { + tax.grand_total_for_current_item = flt(item.amount + current_tax_amount, + precision("total", tax)); + } else { + tax.grand_total_for_current_item = + flt(me.frm.tax_doclist[i-1].grand_total_for_current_item + current_tax_amount, + precision("total", tax)); + } + + // in tax.total, accumulate grand total for each item + tax.total += tax.grand_total_for_current_item; + }); + }); + }, + + get_current_tax_amount: function(item, tax, item_tax_map) { + var tax_rate = this._get_tax_rate(tax, item_tax_map); + var current_tax_amount = 0.0; + + if(tax.charge_type == "Actual") { + // distribute the tax amount proportionally to each item row + var actual = flt(tax.rate, precision("tax_amount", tax)); + current_tax_amount = this.frm.doc.net_total ? + ((item.amount / this.frm.doc.net_total) * actual) : + 0.0; + + } else if(tax.charge_type == "On Net Total") { + current_tax_amount = (tax_rate / 100.0) * item.amount; + + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_amount = (tax_rate / 100.0) * + this.frm.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item; + + } else if(tax.charge_type == "On Previous Row Total") { + current_tax_amount = (tax_rate / 100.0) * + this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; + + } + + return flt(current_tax_amount, precision("tax_amount", tax)); + }, + + _cleanup: function() { + $.each(this.frm.tax_doclist, function(i, tax) { + $.each(["tax_amount_for_current_item", "grand_total_for_current_item", + "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"], + function(i, fieldname) { delete tax[fieldname]; }); + + tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail); + }); + }, + + calculate_total_advance: function(parenttype, advance_parentfield) { + if(this.frm.doc.doctype == parenttype && this.frm.doc.docstatus < 2) { + var advance_doclist = wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: advance_parentfield}); + this.frm.doc.total_advance = flt(wn.utils.sum( + $.map(advance_doclist, function(adv) { return adv.allocated_amount }) + ), precision("total_advance")); + + this.calculate_outstanding_amount(); + } + }, + + _set_in_company_currency: function(item, print_field, base_field) { + // set values in base currency + item[base_field] = flt(item[print_field] * this.frm.doc.conversion_rate, + precision(base_field, item)); + }, +}); \ No newline at end of file diff --git a/public/js/utils.js b/public/js/utils.js index 4df9555ae9b..0284670761f 100644 --- a/public/js/utils.js +++ b/public/js/utils.js @@ -13,8 +13,7 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . - -wn.provide('erpnext.utils'); +wn.provide("erpnext.utils"); erpnext.get_currency = function(company) { if(!company && cur_frm) @@ -23,4 +22,4 @@ erpnext.get_currency = function(company) { return wn.model.get(":Company", company).default_currency || wn.boot.sysdefaults.currency; else return wn.boot.sysdefaults.currency; -} \ No newline at end of file +} diff --git a/selling/Print Format/Quotation Classic/Quotation Classic.txt b/selling/Print Format/Quotation Classic/Quotation Classic.txt index 03a056b18c8..0a633093546 100644 --- a/selling/Print Format/Quotation Classic/Quotation Classic.txt +++ b/selling/Print Format/Quotation Classic/Quotation Classic.txt @@ -1,15 +1,15 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-04-02 18:14:21", + "modified": "2013-05-28 17:17:05", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Quotation", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", "print_format_type": "Client", diff --git a/selling/Print Format/Quotation Modern/Quotation Modern.txt b/selling/Print Format/Quotation Modern/Quotation Modern.txt index b7a29bf4c5d..0f841aa94d0 100644 --- a/selling/Print Format/Quotation Modern/Quotation Modern.txt +++ b/selling/Print Format/Quotation Modern/Quotation Modern.txt @@ -1,15 +1,15 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-04-02 18:14:05", + "modified": "2013-05-28 17:18:02", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Quotation", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", "print_format_type": "Client", diff --git a/selling/Print Format/Quotation Spartan/Quotation Spartan.txt b/selling/Print Format/Quotation Spartan/Quotation Spartan.txt index 3fbf6291c30..d2bfcd0737e 100644 --- a/selling/Print Format/Quotation Spartan/Quotation Spartan.txt +++ b/selling/Print Format/Quotation Spartan/Quotation Spartan.txt @@ -1,15 +1,15 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-04-02 18:13:47", + "modified": "2013-05-28 17:18:38", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Quotation", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", "print_format_type": "Client", diff --git a/selling/Print Format/Sales Order Classic/Sales Order Classic.txt b/selling/Print Format/Sales Order Classic/Sales Order Classic.txt index 9415035aaf8..28e3af07eb6 100644 --- a/selling/Print Format/Sales Order Classic/Sales Order Classic.txt +++ b/selling/Print Format/Sales Order Classic/Sales Order Classic.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-01-25 17:18:37", + "modified": "2013-05-28 17:20:59", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Order", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/selling/Print Format/Sales Order Modern/Sales Order Modern.txt b/selling/Print Format/Sales Order Modern/Sales Order Modern.txt index f222d5bad28..5237068e40f 100644 --- a/selling/Print Format/Sales Order Modern/Sales Order Modern.txt +++ b/selling/Print Format/Sales Order Modern/Sales Order Modern.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-01-25 17:18:20", + "modified": "2013-05-28 17:21:05", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Order", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/selling/Print Format/Sales Order Spartan/Sales Order Spartan.txt b/selling/Print Format/Sales Order Spartan/Sales Order Spartan.txt index 3db5f05f09b..f1f5d921ca3 100644 --- a/selling/Print Format/Sales Order Spartan/Sales Order Spartan.txt +++ b/selling/Print Format/Sales Order Spartan/Sales Order Spartan.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-01-25 17:19:08", + "modified": "2013-05-28 17:20:50", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Sales Order", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Selling", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/selling/doctype/quotation/quotation.js b/selling/doctype/quotation/quotation.js index 37ff7d41c3b..42761936965 100644 --- a/selling/doctype/quotation/quotation.js +++ b/selling/doctype/quotation/quotation.js @@ -21,108 +21,52 @@ cur_frm.cscript.other_fname = "other_charges"; cur_frm.cscript.sales_team_fname = "sales_team"; // ===================================================================================== -wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/selling/doctype/sales_common/sales_common.js'); -// ONLOAD -// =================================================================================== -cur_frm.cscript.onload = function(doc, cdt, cdn) { - cur_frm.cscript.manage_rounded_total(); - if(!doc.quotation_to) - hide_field(['customer','customer_address','contact_person','customer_name','lead', - 'address_display', 'contact_display', 'contact_mobile', 'contact_email', - 'territory', 'customer_group']); - if(!doc.price_list_name) set_multiple(cdt,cdn,{price_list_name:sys_defaults.price_list_name}); - if(!doc.status) set_multiple(cdt,cdn,{status:'Draft'}); - if(!doc.transaction_date) set_multiple(cdt,cdn,{transaction_date:get_today()}); - if(!doc.conversion_rate) set_multiple(cdt,cdn,{conversion_rate:'1.00'}); - if(!doc.currency && sys_defaults.currency) set_multiple(cdt,cdn,{currency:sys_defaults.currency}); - if(!doc.price_list_currency) set_multiple(cdt, cdn, {price_list_currency: doc.currency, plc_conversion_rate: 1}); - - if(!doc.company && sys_defaults.company) set_multiple(cdt,cdn,{company:sys_defaults.company}); - if(!doc.fiscal_year && sys_defaults.fiscal_year) set_multiple(cdt,cdn,{fiscal_year:sys_defaults.fiscal_year}); - - if(doc.quotation_to) { - if(doc.quotation_to == 'Customer') { - hide_field('lead'); +erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({ + refresh: function(doc, dt, dn) { + this._super(); + + if(doc.docstatus == 1 && doc.status!='Order Lost') { + cur_frm.add_custom_button('Make Sales Order', cur_frm.cscript['Make Sales Order']); + cur_frm.add_custom_button('Set as Lost', cur_frm.cscript['Declare Order Lost']); + cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); } - else if (doc.quotation_to == 'Lead') { - hide_field(['customer', 'customer_address', 'contact_person', 'customer_group']); + + if (!doc.__islocal) { + cur_frm.communication_view = new wn.views.CommunicationList({ + list: wn.model.get("Communication", {"quotation": doc.name}), + parent: cur_frm.fields_dict.communication_html.wrapper, + doc: doc, + recipients: doc.contact_email + }); } - } -} - -cur_frm.cscript.onload_post_render = function(doc, dt, dn) { - var callback = function(doc, dt, dn) { - // defined in sales_common.js - cur_frm.cscript.update_item_details(doc, dt, dn); - } - cur_frm.cscript.hide_price_list_currency(doc, dt, dn, callback); -} - -// hide - unhide fields based on lead or customer.. -// ======================================================================================================================= -cur_frm.cscript.lead_cust_show = function(doc,cdt,cdn){ - hide_field(['lead', 'customer','customer_address','contact_person', - 'customer_name','address_display','contact_display','contact_mobile','contact_email', - 'territory','customer_group']); - if(doc.quotation_to == 'Lead') unhide_field(['lead']); - else if(doc.quotation_to == 'Customer') unhide_field(['customer']); + + this.quotation_to(); + }, - doc.lead = doc.customer = doc.customer_name = doc.customer_address = doc.contact_person = doc.address_display = doc.contact_display = doc.contact_mobile = doc.contact_email = doc.territory = doc.customer_group = ""; -} - - - -//================ hide - unhide fields on basis of quotation to either lead or customer =============================== -cur_frm.cscript.quotation_to = function(doc,cdt,cdn){ - cur_frm.cscript.lead_cust_show(doc,cdt,cdn); -} - - -// REFRESH -// =================================================================================== -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - cur_frm.clear_custom_buttons(); - - if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn); - - - if(doc.docstatus == 1 && doc.status!='Order Lost') { - cur_frm.add_custom_button('Make Sales Order', cur_frm.cscript['Make Sales Order']); - cur_frm.add_custom_button('Set as Lost', cur_frm.cscript['Declare Order Lost']); - cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); - } - - erpnext.hide_naming_series(); - cur_frm.toggle_display("contact_section", doc.customer || doc.lead); + quotation_to: function() { + this.frm.toggle_reqd("lead", this.frm.doc.quotation_to == "Lead"); + this.frm.toggle_reqd("customer", this.frm.doc.quotation_to == "Customer"); + }, - if (!doc.__islocal) { - cur_frm.communication_view = new wn.views.CommunicationList({ - list: wn.model.get("Communication", {"quotation": doc.name}), - parent: cur_frm.fields_dict.communication_html.wrapper, - doc: doc, - recipients: doc.contact_email - }); - } -} + validate_company_and_party: function(party_field) { + if(this.frm.doc.quotation_to == "Lead") { + return true; + } else if(!this.frm.doc.quotation_to) { + msgprint(wn._("Please select a value for" + " " + wn.meta.get_label(this.frm.doc.doctype, + "quotation_to", this.frm.doc.name))); + return false; + } else { + return this._super(party_field); + } + }, +}); - -//customer -cur_frm.cscript.customer = function(doc,dt,dn) { - var pl = doc.price_list_name; - var callback = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - cur_frm.refresh(); - if (pl != doc.price_list_name) cur_frm.cscript.price_list_name(doc, dt, dn); - } - - if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), - 'get_default_customer_address', '', callback); - if(doc.customer) unhide_field(['customer_address','contact_person','territory', 'customer_group']); - cur_frm.toggle_display("contact_section", doc.customer || doc.lead); -} +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, new erpnext.selling.QuotationController({frm: cur_frm})); cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({ @@ -135,8 +79,6 @@ cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc cur_frm.fields_dict.lead.get_query = erpnext.utils.lead_query; cur_frm.cscript.lead = function(doc, cdt, cdn) { - cur_frm.toggle_display("contact_section", doc.customer || doc.lead); - if(doc.lead) { get_server_fields('get_lead_details', doc.lead,'', doc, cdt, cdn, 1); unhide_field('territory'); @@ -247,31 +189,6 @@ cur_frm.cscript['Declare Order Lost'] = function(){ qtn_lost_dialog.show(); } -//===================== Quotation to validation - either customer or lead mandatory ==================== -cur_frm.cscript.quot_to_validate = function(doc,cdt,cdn){ - - if(doc.quotation_to == 'Lead'){ - - if(!doc.lead){ - alert("Lead is mandatory."); - validated = false; - } - } - else if(doc.quotation_to == 'Customer'){ - if(!doc.customer){ - alert("Customer is mandatory."); - validated = false; - } - } -} - -//===================validation function ================================= - -cur_frm.cscript.validate = function(doc,cdt,cdn){ - cur_frm.cscript.recalculate_values(doc, cdt, cdn); - cur_frm.cscript.quot_to_validate(doc,cdt,cdn); -} - //================ Last Quoted Price and Last Sold Price suggestion ====================== cur_frm.fields_dict['quotation_details'].grid.get_field('item_code').get_query= function(doc, cdt, cdn) { var d = locals[cdt][cdn]; diff --git a/selling/doctype/quotation/quotation.py b/selling/doctype/quotation/quotation.py index c154a6a3b0c..f4c30fcb93d 100644 --- a/selling/doctype/quotation/quotation.py +++ b/selling/doctype/quotation/quotation.py @@ -95,17 +95,6 @@ class DocType(SellingController): def get_rate(self,arg): return get_obj('Sales Common').get_rate(arg) - # Load Default Charges - # ---------------------------------------------------------- - def load_default_taxes(self): - self.doclist = get_obj('Sales Common').load_default_taxes(self) - - # Pull details from other charges master (Get Sales Taxes and Charges Master) - # ---------------------------------------------------------- - def get_other_charges(self): - self.doclist = get_obj('Sales Common').get_other_charges(self) - - # GET TERMS AND CONDITIONS # ==================================================================================== def get_tc_details(self): @@ -142,6 +131,8 @@ class DocType(SellingController): #do not allow sales item in maintenance quotation and service item in sales quotation #----------------------------------------------------------------------------------------------- def validate_order_type(self): + super(DocType, self).validate_order_type() + if self.doc.order_type in ['Maintenance', 'Service']: for d in getlist(self.doclist, 'quotation_details'): is_service_item = sql("select is_service_item from `tabItem` where name=%s", d.item_code) diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt index 4115be833b4..322be88b6ec 100644 --- a/selling/doctype/quotation/quotation.txt +++ b/selling/doctype/quotation/quotation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-22 12:10:46", + "creation": "2013-05-24 19:29:08", "docstatus": 0, - "modified": "2013-05-22 16:54:07", + "modified": "2013-05-28 14:50:59", "modified_by": "Administrator", "owner": "Administrator" }, @@ -78,6 +78,7 @@ "reqd": 1 }, { + "depends_on": "eval:doc.quotation_to == \"Customer\"", "doctype": "DocField", "fieldname": "customer", "fieldtype": "Link", @@ -92,6 +93,7 @@ "search_index": 1 }, { + "depends_on": "eval:doc.quotation_to == \"Lead\"", "doctype": "DocField", "fieldname": "lead", "fieldtype": "Link", @@ -105,19 +107,21 @@ "read_only": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Customer Name", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "in_filter": 0, "label": "Address", "oldfieldname": "customer_address", @@ -128,29 +132,32 @@ "search_index": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "in_filter": 0, "label": "Contact", "print_hide": 0, "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "print_hide": 0, "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -214,6 +221,7 @@ "oldfieldtype": "Table", "options": "Quotation Item", "read_only": 0, + "reqd": 1, "width": "40px" }, { @@ -239,11 +247,19 @@ "oldfieldname": "net_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 0, + "print_hide": 1, "read_only": 1, "reqd": 0, "width": "100px" }, + { + "doctype": "DocField", + "fieldname": "net_total_export", + "fieldtype": "Currency", + "label": "Net Total (Export)", + "options": "currency", + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "recalculate_values", @@ -423,13 +439,22 @@ "doctype": "DocField", "fieldname": "other_charges_total", "fieldtype": "Currency", - "label": "Taxes and Charges Total*", + "label": "Taxes and Charges Total", "oldfieldname": "other_charges_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1 }, + { + "doctype": "DocField", + "fieldname": "other_charges_total_export", + "fieldtype": "Currency", + "label": "Taxes and Charges Total (Export)", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "other_charges_calculation", @@ -596,6 +621,7 @@ "read_only": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "col_break98", "fieldtype": "Column Break", @@ -649,6 +675,7 @@ "search_index": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_group", "fieldtype": "Link", @@ -819,19 +846,6 @@ "read_only": 1, "width": "150px" }, - { - "description": "The date at which current entry is corrected in the system.", - "doctype": "DocField", - "fieldname": "amendment_date", - "fieldtype": "Date", - "label": "Amendment Date", - "no_copy": 1, - "oldfieldname": "amendment_date", - "oldfieldtype": "Date", - "print_hide": 1, - "read_only": 0, - "width": "100px" - }, { "doctype": "DocField", "fieldname": "communication_history", @@ -868,23 +882,28 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "role": "Sales User", + "role": "Sales Manager", "submit": 1, "write": 1 }, - { - "doctype": "DocPerm", - "role": "Customer" - }, { "amend": 1, "cancel": 1, "create": 1, "doctype": "DocPerm", - "role": "Sales Manager", + "role": "Sales User", "submit": 1, "write": 1 }, + { + "amend": 0, + "cancel": 0, + "create": 0, + "doctype": "DocPerm", + "role": "Customer", + "submit": 0, + "write": 0 + }, { "amend": 1, "cancel": 1, diff --git a/selling/doctype/quotation_item/quotation_item.txt b/selling/doctype/quotation_item/quotation_item.txt index bcb9281cb96..11ac1bae108 100644 --- a/selling/doctype/quotation_item/quotation_item.txt +++ b/selling/doctype/quotation_item/quotation_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 11:42:57", "docstatus": 0, - "modified": "2013-05-22 12:08:32", + "modified": "2013-05-22 12:10:32", "modified_by": "Administrator", "owner": "Administrator" }, @@ -119,7 +119,7 @@ "options": "currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "reqd": 0, "width": "100px" }, @@ -195,7 +195,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "reqd": 0, "search_index": 0, "width": "100px" diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js index 8a8d8d0a6c7..0b0b3290d7d 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -21,118 +21,474 @@ // cur_frm.cscript.other_fname - wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); fieldname // cur_frm.cscript.sales_team_fname - Sales Team fieldname -// ============== Load Default Taxes =================== -cur_frm.cscript.load_taxes = function(doc, cdt, cdn, callback) { - // run if this is not executed from dt_map... - doc = locals[doc.doctype][doc.name]; - if(doc.customer || getchildren('Sales Taxes and Charges', doc.name, 'other_charges', doc.doctype).length) { - if(callback) { - callback(doc, cdt, cdn); +wn.provide("erpnext.selling"); +wn.require("app/js/transaction.js"); + +erpnext.selling.SellingController = erpnext.TransactionController.extend({ + setup: function() { + this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate"); + }, + + onload: function() { + this._super(); + this.toggle_rounded_total(); + }, + + customer: function() { + var me = this; + if(this.frm.doc.customer || this.frm.doc.debit_to) { + if(!this.frm.doc.company) { + this.frm.set_value("customer", null); + msgprint(wn._("Please specify Company")); + } else { + var price_list_name = this.frm.doc.price_list_name; + this.frm.call({ + doc: this.frm.doc, + method: "set_customer_defaults", + freeze: true, + callback: function(r) { + if(!r.exc) { + me.frm.refresh_fields(); + (me.frm.doc.price_list_name !== price_list_name) ? + me.price_list_name() : + me.price_list_currency(); + } + } + }); + } } - } else if(doc.charge) { - cur_frm.cscript.get_charges(doc, cdt, cdn, callback); - } else { - $c_obj(make_doclist(doc.doctype, doc.name),'load_default_taxes','',function(r,rt){ - refresh_field('other_charges'); - if(callback) callback(doc, cdt, cdn); + }, + + barcode: function(doc, cdt, cdn) { + this.item_code(doc, cdt, cdn); + }, + + item_code: function(doc, cdt, cdn) { + var me = this; + var item = wn.model.get_doc(cdt, cdn); + if(item.item_code || item.barcode) { + if(!this.validate_company_and_party("customer")) { + item.item_code = null; + refresh_field("item_code", item.name, item.parentfield); + } else { + this.frm.call({ + method: "selling.utils.get_item_details", + child: item, + args: { + args: { + item_code: item.item_code, + barcode: item.barcode, + warehouse: item.warehouse, + doctype: me.frm.doc.doctype, + customer: me.frm.doc.customer, + currency: me.frm.doc.currency, + conversion_rate: me.frm.doc.conversion_rate, + price_list_name: me.frm.doc.price_list_name, + price_list_currency: me.frm.doc.price_list_currency, + plc_conversion_rate: me.frm.doc.plc_conversion_rate, + company: me.frm.doc.company, + order_type: me.frm.doc.order_type, + is_pos: cint(me.frm.doc.is_pos), + update_stock: cint(me.frm.doc.update_stock), + } + }, + callback: function(r) { + if(!r.exc) { + me.ref_rate(me.frm.doc, cdt, cdn); + } + } + }); + } + } + }, + + price_list_name: function() { + this._super("selling"); + }, + + ref_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["ref_rate", "adj_rate"]); + + item.export_rate = flt(item.ref_rate * (1 - item.adj_rate / 100.0), + precision("export_rate", item)); + + this.calculate_taxes_and_totals(); + }, + + adj_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + if(!item.ref_rate) { + item.adj_rate = 0.0; + } else { + this.ref_rate(doc, cdt, cdn); + } + }, + + export_rate: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["export_rate", "ref_rate"]); + + if(item.ref_rate) { + item.adj_rate = flt((1 - item.export_rate / item.ref_rate) * 100.0, + precision("adj_rate", item)); + } else { + item.adj_rate = 0.0; + } + + this.calculate_taxes_and_totals(); + }, + + commission_rate: function() { + this.calculate_commission(); + refresh_field("total_commission"); + }, + + total_commission: function() { + if(this.frm.doc.net_total) { + wn.model.round_floats_in(this.frm.doc, ["net_total", "total_commission"]); + + if(this.frm.doc.net_total < this.frm.doc.total_commission) { + var msg = (wn._("[Error]") + " " + + wn._(wn.meta.get_label(this.frm.doc.doctype, "total_commission", + this.frm.doc.name)) + " > " + + wn._(wn.meta.get_label(this.frm.doc.doctype, "net_total", this.frm.doc.name))); + msgprint(msg); + throw msg; + } + + this.frm.set_value("commission_rate", + flt(this.frm.doc.total_commission * 100.0 / this.frm.doc.net_total)); + } + }, + + allocated_percentage: function(doc, cdt, cdn) { + var sales_person = wn.model.get_doc(cdt, cdn); + + if(sales_person.allocated_percentage) { + sales_person.allocated_percentage = flt(sales_person.allocated_percentage, + precision("allocated_percentage", sales_person)); + sales_person.allocated_amount = flt(this.frm.doc.net_total * + sales_person.allocated_percentage / 100.0, + precision("allocated_amount", sales_person)); + + refresh_field(["allocated_percentage", "allocated_amount"], sales_person.name, + sales_person.parentfield); + } + }, + + warehouse: function(doc, cdt, cdn) { + var item = webnotes.get_doc(cdt, cdn); + if(item.item_code && (item.warehouse || item.reserved_warehouse)) { + this.frm.call({ + method: "selling.utils.get_available_qty", + child: item, + args: { + item_code: item.item_code, + warehouse: item.warehouse || item.reserved_warehouse, + }, + }); + } + }, + + toggle_rounded_total: function() { + var me = this; + if(cint(wn.defaults.get_global_default("disable_rounded_total"))) { + $.each(["rounded_total", "rounded_total_export"], function(i, fieldname) { + me.frm.set_df_property(fieldname, "print_hide", 1); + me.frm.toggle_display(fieldname, false); + }); + } + }, + + calculate_taxes_and_totals: function() { + this._super(); + this.calculate_total_advance("Sales Invoice", "advance_adjustment_details"); + this.calculate_commission(); + this.calculate_contribution(); + + // TODO check for custom_recalc in custom scripts of server + + this.frm.refresh_fields(); + }, + + calculate_item_values: function() { + var me = this; + $.each(this.frm.item_doclist, function(i, item) { + wn.model.round_floats_in(item); + item.export_amount = flt(item.export_rate * item.qty, precision("export_amount", item)); + + me._set_in_company_currency(item, "ref_rate", "base_ref_rate"); + me._set_in_company_currency(item, "export_rate", "basic_rate"); + me._set_in_company_currency(item, "export_amount", "amount"); }); - } -} - - -// Gets called after existing item details are update to fill in -// remaining default values -cur_frm.cscript.load_defaults = function(doc, dt, dn, callback) { - if(!cur_frm.doc.__islocal) { return; } - - doc = locals[doc.doctype][doc.name]; - var fields_to_refresh = wn.model.set_default_values(doc); - if(fields_to_refresh) { refresh_many(fields_to_refresh); } - - fields_to_refresh = null; - var children = getchildren(cur_frm.cscript.tname, doc.name, cur_frm.cscript.fname); - if(!children) { return; } - for(var i=0; i '+ base_curr + ')'; - cur_frm.fields_dict['plc_conversion_rate'].label_span.innerHTML = 'Price List Currency Conversion Rate (' + doc.price_list_currency +' -> '+ base_curr + ')'; - - if (doc.doctype == 'Sales Invoice') { - si_cols = {'total_advance': 'Total Advance', 'outstanding_amount': 'Outstanding Amount', 'paid_amount': 'Paid Amount', 'write_off_amount': 'Write Off Amount'} - for (d in si_cols) cur_frm.fields_dict[d].label_span.innerHTML = si_cols[d] + ' (' + base_curr + ')'; - } -} - - -var set_dynamic_label_child = function(doc, cdt, cdn, base_curr) { - // item table flds - item_cols_base = {'basic_rate': 'Basic Rate', 'base_ref_rate': 'Price List Rate', 'amount': 'Amount'}; - item_cols_export = {'export_rate': 'Basic Rate', 'ref_rate': 'Price List Rate', 'export_amount': 'Amount'}; + }, + + get_current_tax_fraction: function(tax, item_tax_map) { + // Get tax fraction for calculating tax exclusive amount + // from tax inclusive amount + var current_tax_fraction = 0.0; - for (d in item_cols_base) $('[data-grid-fieldname="'+cur_frm.cscript.tname+'-'+d+'"]').html(item_cols_base[d]+' ('+base_curr+')'); - for (d in item_cols_export) $('[data-grid-fieldname="'+cur_frm.cscript.tname+'-'+d+'"]').html(item_cols_export[d]+' ('+doc.currency+')'); - - var hide = (doc.currency == sys_defaults['currency']) ? false : true; - for (f in item_cols_base) { - cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp(f, hide); - } - - //tax table flds - tax_cols = {'tax_amount': 'Amount', 'total': 'Total'}; - for (d in tax_cols) $('[data-grid-fieldname="Sales Taxes and Charges-'+d+'"]').html(tax_cols[d]+' ('+base_curr+')'); + if(cint(tax.included_in_print_rate)) { + var tax_rate = this._get_tax_rate(tax, item_tax_map); + + if(tax.charge_type == "On Net Total") { + current_tax_fraction = (tax_rate / 100.0); + + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_fraction = (tax_rate / 100.0) * + this.frm.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item; + + } else if(tax.charge_type == "On Previous Row Total") { + current_tax_fraction = (tax_rate / 100.0) * + this.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item; + } + } - if (doc.doctype == 'Sales Invoice') { - // advance table flds - adv_cols = {'advance_amount': 'Advance Amount', 'allocated_amount': 'Allocated Amount'} - for (d in adv_cols) $('[data-grid-fieldname="Sales Invoice Advance-'+d+'"]').html(adv_cols[d]+' ('+base_curr+')'); - } -} + return current_tax_fraction; + }, + + calculate_net_total: function() { + var me = this; -// Change label dynamically based on currency -//------------------------------------------------------------------ + this.frm.doc.net_total = this.frm.doc.net_total_export = 0.0; + $.each(this.frm.item_doclist, function(i, item) { + me.frm.doc.net_total += item.amount; + me.frm.doc.net_total_export += item.export_amount; + }); + + wn.model.round_floats_in(this.frm.doc, ["net_total", "net_total_export"]); + }, + + calculate_totals: function() { + var tax_count = this.frm.tax_doclist.length; + this.frm.doc.grand_total = flt( + tax_count ? this.frm.tax_doclist[tax_count - 1].total : this.frm.doc.net_total, + precision("grand_total")); + this.frm.doc.grand_total_export = flt(this.frm.doc.grand_total / this.frm.doc.conversion_rate, + precision("grand_total_export")); + + this.frm.doc.other_charges_total = flt(this.frm.doc.grand_total - this.frm.doc.net_total, + precision("other_charges_total")); + this.frm.doc.other_charges_total_export = flt( + this.frm.doc.grand_total_export - this.frm.doc.net_total_export, + precision("other_charges_total_export")); + + this.frm.doc.rounded_total = Math.round(this.frm.doc.grand_total); + this.frm.doc.rounded_total_export = Math.round(this.frm.doc.grand_total_export); + }, + + calculate_outstanding_amount: function() { + // NOTE: + // write_off_amount is only for POS Invoice + // total_advance is only for non POS Invoice + if(this.frm.doc.doctype == "Sales Invoice" && this.frm.doc.docstatus < 2) { + wn.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount", + "paid_amount"]); + var total_amount_to_pay = this.frm.doc.grand_total - this.frm.doc.write_off_amount; + this.frm.doc.outstanding_amount = flt(total_amount_to_pay - this.frm.doc.total_advance - + this.frm.doc.paid_amount, precision("outstanding_amount")); + } + }, + + calculate_commission: function() { + if(this.frm.fields_dict.commission_rate) { + if(this.frm.doc.commission_rate > 100) { + var msg = wn._(wn.meta.get_label(this.frm.doc.doctype, "commission_rate", this.frm.doc.name)) + + " " + wn._("cannot be greater than 100"); + msgprint(msg); + throw msg; + } + + this.frm.doc.total_commission = flt(this.frm.doc.net_total * this.frm.doc.commission_rate / 100.0, + precision("total_commission")); + } + }, + + calculate_contribution: function() { + var me = this; + $.each(wn.model.get_doclist(this.frm.doc.doctype, this.frm.doc.name, + {parentfield: "sales_team"}), function(i, sales_person) { + wn.model.round_floats_in(sales_person); + if(sales_person.allocated_percentage) { + sales_person.allocated_amount = flt( + me.frm.doc.net_total * sales_person.allocated_percentage / 100.0, + precision("allocated_amount", sales_person)); + } + }); + }, + + _cleanup: function() { + this._super(); + this.frm.doc.in_words = this.frm.doc.in_words_export = ""; + }, -cur_frm.cscript.dynamic_label = function(doc, cdt, cdn, base_curr, callback) { - cur_frm.cscript.base_currency = base_curr; - set_dynamic_label_par(doc, cdt, cdn, base_curr); - set_dynamic_label_child(doc, cdt, cdn, base_curr); - set_sales_bom_help(doc); + show_item_wise_taxes: function() { + if(this.frm.fields_dict.other_charges_calculation) { + $(this.get_item_wise_taxes_html()) + .appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty()); + } + }, + + get_charges: function() { + var me = this; + if(this.frm.doc.charge) { + this.frm.call({ + doc: this.frm.doc, + method: "get_other_charges", + callback: function(r) { + if(!r.exc) { + me.calculate_taxes_and_totals(); + } + } + }); + } + }, + + set_dynamic_labels: function() { + var company_currency = this.get_company_currency(); + + this.change_form_labels(company_currency); + this.change_grid_labels(company_currency); + }, + + change_form_labels: function(company_currency) { + var me = this; + var field_label_map = {}; + + var setup_field_label_map = function(fields_list, currency) { + $.each(fields_list, function(i, fname) { + var docfield = wn.meta.get_docfield(me.frm.doc.doctype, fname); + if(docfield) { + var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); + field_label_map[fname] = label.trim() + " (" + currency + ")"; + } + }); + }; + + setup_field_label_map(["net_total", "other_charges_total", "grand_total", + "rounded_total", "in_words", + "outstanding_amount", "total_advance", "paid_amount", "write_off_amount"], + company_currency); + + setup_field_label_map(["net_total_export", "other_charges_total_export", "grand_total_export", + "rounded_total_export", "in_words_export"], this.frm.doc.currency); + + setup_field_label_map(["conversion_rate"], "1 " + this.frm.doc.currency + + " = [?] " + company_currency); + + if(this.frm.doc.price_list_currency && this.frm.doc.price_list_currency!=company_currency) { + setup_field_label_map(["plc_conversion_rate"], "1 " + this.frm.doc.price_list_currency + + " = [?] " + company_currency); + } + + // toggle fields + this.frm.toggle_display(["conversion_rate", "net_total", "other_charges_total", + "grand_total", "rounded_total", "in_words"], + this.frm.doc.currency != company_currency); + + this.frm.toggle_display(["plc_conversion_rate"], + this.frm.doc.price_list_currency != company_currency); + + // set labels + $.each(field_label_map, function(fname, label) { + me.frm.fields_dict[fname].set_label(label); + }); + }, + + change_grid_labels: function(company_currency) { + var me = this; + var field_label_map = {}; + + var setup_field_label_map = function(fields_list, currency, parentfield) { + var grid_doctype = me.frm.fields_dict[parentfield].grid.doctype; + $.each(fields_list, function(i, fname) { + var docfield = wn.meta.get_docfield(grid_doctype, fname); + if(docfield) { + var label = wn._(docfield.label || "").replace(/\([^\)]*\)/g, ""); + field_label_map[grid_doctype + "-" + fname] = + label.trim() + " (" + currency + ")"; + } + }); + } + + setup_field_label_map(["basic_rate", "base_ref_rate", "amount"], + company_currency, this.fname); + + setup_field_label_map(["export_rate", "ref_rate", "export_amount"], + this.frm.doc.currency, this.fname); + + setup_field_label_map(["tax_amount", "total"], company_currency, "other_charges"); + + if(this.frm.fields_dict["advance_allocation_details"]) { + setup_field_label_map(["advance_amount", "allocated_amount"], company_currency, + "advance_allocation_details"); + } + + // toggle columns + var item_grid = this.frm.fields_dict[this.fname].grid; + var show = this.frm.doc.currency != company_currency; + $.each(["basic_rate", "base_ref_rate", "amount"], function(i, fname) { + if(wn.meta.get_docfield(item_grid.doctype, fname)) + item_grid.set_column_disp(fname, show); + }); + + // set labels + var $wrapper = $(this.frm.wrapper); + $.each(field_label_map, function(fname, label) { + $wrapper.find('[data-grid-fieldname="'+fname+'"]').text(label); + }); + }, + +}); - if (callback) callback(doc, cdt, cdn); -} +// to save previous state of cur_frm.cscript +var prev_cscript = {}; +$.extend(prev_cscript, cur_frm.cscript); + +cur_frm.cscript = new erpnext.selling.SellingController({frm: cur_frm}); + +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, prev_cscript); // Help for Sales BOM items var set_sales_bom_help = function(doc) { @@ -158,113 +514,8 @@ var set_sales_bom_help = function(doc) { refresh_field('sales_bom_help'); } - -// hide / unhide price list currency based on availability of price list in customer's currency -//--------------------------------------------------------------------------------------------------- - -cur_frm.cscript.hide_price_list_currency = function(doc, cdt, cdn, callback1) { - if (doc.price_list_name && doc.currency) { - wn.call({ - method: 'selling.doctype.sales_common.sales_common.get_price_list_currency', - args: {'price_list':doc.price_list_name, 'company': doc.company}, - callback: function(r, rt) { - pl_currency = r.message[0]?r.message[0]:[]; - unhide_field(['price_list_currency', 'plc_conversion_rate']); - - if (pl_currency.length==1) { - if (doc.price_list_currency != pl_currency[0]) - set_multiple(cdt, cdn, {price_list_currency:pl_currency[0]}); - if (pl_currency[0] == doc.currency) { - if(doc.plc_conversion_rate != doc.conversion_rate) - set_multiple(cdt, cdn, {plc_conversion_rate:doc.conversion_rate}); - hide_field(['price_list_currency', 'plc_conversion_rate']); - } else if (pl_currency[0] == r.message[1]) { - if (doc.plc_conversion_rate != 1) - set_multiple(cdt, cdn, {plc_conversion_rate:1}) - hide_field(['price_list_currency', 'plc_conversion_rate']); - } - } - - if (r.message[1] == doc.currency) { - if (doc.conversion_rate != 1) - set_multiple(cdt, cdn, {conversion_rate:1}); - hide_field(['conversion_rate', 'grand_total_export', 'in_words_export', 'rounded_total_export']); - } else { - unhide_field(['conversion_rate', 'grand_total_export', 'in_words_export']); - if(!cint(sys_defaults.disable_rounded_total)) - unhide_field("rounded_total_export"); - } - if (r.message[1] == doc.price_list_currency) { - if (doc.plc_conversion_rate != 1) - set_multiple(cdt, cdn, {plc_conversion_rate:1}); - hide_field('plc_conversion_rate'); - } else unhide_field('plc_conversion_rate'); - cur_frm.cscript.dynamic_label(doc, cdt, cdn, r.message[1], callback1); - } - }) - } -} - -cur_frm.cscript.manage_rounded_total = function() { - if(cint(sys_defaults.disable_rounded_total)) { - cur_frm.set_df_property("rounded_total", "print_hide", 1); - cur_frm.set_df_property("rounded_total_export", "print_hide", 1); - hide_field(["rounded_total", "rounded_total_export"]); - } -} - -// TRIGGERS FOR CALCULATIONS -// ===================================================================================================== - -// ********************* CURRENCY ****************************** -cur_frm.cscript.currency = function(doc, cdt, cdn) { - cur_frm.cscript.price_list_name(doc, cdt, cdn); -} - -cur_frm.cscript.price_list_currency = cur_frm.cscript.currency; -cur_frm.cscript.conversion_rate = cur_frm.cscript.currency; -cur_frm.cscript.plc_conversion_rate = cur_frm.cscript.currency; - -cur_frm.cscript.company = function(doc, cdt, cdn) { - wn.call({ - method: 'selling.doctype.sales_common.sales_common.get_comp_base_currency', - args: {company:doc.company}, - callback: function(r, rt) { - var doc = locals[cdt][cdn]; - set_multiple(doc.doctype, doc.name, { - currency:r.message, - price_list_currency:r.message - }); - cur_frm.cscript.currency(doc, cdt, cdn); - } - }); -} - - - -// ******************** PRICE LIST ****************************** -cur_frm.cscript.price_list_name = function(doc, cdt, cdn) { - var callback = function() { - var fname = cur_frm.cscript.fname; - var cl = getchildren(cur_frm.cscript.tname, doc.name, cur_frm.cscript.fname); - if(doc.price_list_name && doc.currency && doc.price_list_currency && doc.conversion_rate && doc.plc_conversion_rate) { - $c_obj(make_doclist(doc.doctype, doc.name), 'get_adj_percent', '', - function(r, rt) { - refresh_field(fname); - var doc = locals[cdt][cdn]; - cur_frm.cscript.recalc(doc,3); //this is to re-calculate BASIC RATE and AMOUNT on basis of changed REF RATE - } - ); - } - } - cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn, callback); -} - - - -// ******************** ITEM CODE ******************************** cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field("item_code").get_query = function(doc, cdt, cdn) { - if (inList(['Maintenance', 'Service'], doc.order_type)) { + if (doc.order_type == "Maintenance") { return erpnext.queries.item({ 'ifnull(tabItem.is_service_item, "No")': 'Yes' }); @@ -275,47 +526,6 @@ cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field("item_code").get_query } } - -cur_frm.cscript.item_code = function(doc, cdt, cdn) { - var fname = cur_frm.cscript.fname; - var d = locals[cdt][cdn]; - if (d.item_code) { - if (!doc.company) { - msgprint("Please select company to proceed"); - d.item_code = ''; - refresh_field('item_code', d.name, fname); - } else { - var callback = function(r, rt){ - cur_frm.cscript.recalc(doc, 1); - } - var args = { - 'item_code':d.item_code, - 'income_account':d.income_account, - 'cost_center': d.cost_center, - 'warehouse': d.warehouse - }; - get_server_fields('get_item_details',JSON.stringify(args), - fname,doc,cdt,cdn,1,callback); - } - } - if(cur_frm.cscript.custom_item_code){ - cur_frm.cscript.custom_item_code(doc, cdt, cdn); - } -} - -//Barcode -// -cur_frm.cscript.barcode = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - var callback = function(r, rt) { - cur_frm.cscript.item_code(doc, cdt, cdn); - } - if(d.barcode) { - get_server_fields('get_barcode_details', d.barcode, cur_frm.cscript.fname, - doc, cdt, cdn, 1, callback); - } -} - cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field('batch_no').get_query = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; @@ -360,66 +570,6 @@ cur_frm.fields_dict['contact_person'].get_query = function(doc, cdt, cdn) { '" AND docstatus != 2 AND %(key)s LIKE "%s" ORDER BY name ASC LIMIT 50'; } -// *********************** QUANTITY *************************** -cur_frm.cscript.qty = function(doc, cdt, cdn) { cur_frm.cscript.recalc(doc, 1); } - -// ************************ DISCOUNT (%) *********************** -cur_frm.cscript.adj_rate = function(doc, cdt, cdn) { cur_frm.cscript.recalc(doc, 1); } - -// ************************ REF RATE **************************** -cur_frm.cscript.ref_rate = function(doc, cdt, cdn){ - var d = locals[cdt][cdn]; - var consider_incl_rate = cur_frm.cscript.consider_incl_rate(doc, cur_frm.cscript.other_fname); - if(!consider_incl_rate) { - set_multiple(cur_frm.cscript.tname, d.name, {'export_rate': flt(d.ref_rate) * (100 - flt(d.adj_rate)) / 100}, cur_frm.cscript.fname); - } - cur_frm.cscript.recalc(doc, 1); -} - -// *********************** BASIC RATE ************************** -cur_frm.cscript.basic_rate = function(doc, cdt, cdn) { - var fname = cur_frm.cscript.fname; - var d = locals[cdt][cdn]; - if(!d.qty) { - d.qty = 1; - refresh_field('qty', d.name, fname); - } - var consider_incl_rate = cur_frm.cscript.consider_incl_rate(doc, cur_frm.cscript.other_fname); - if(!consider_incl_rate) { - cur_frm.cscript.recalc(doc, 2); - } else { - var basic_rate = cur_frm.cscript.back_calc_basic_rate( - doc, cur_frm.cscript.tname, fname, d, cur_frm.cscript.other_fname - ); - // TODO: remove roundNumber for basic_rate comparison - if (d.basic_rate != roundNumber(basic_rate, 2)) { - d.basic_rate = basic_rate; - refresh_field('basic_rate', d.name, fname); - msgprint("You cannot change Basic Rate* (Base Currency) when \ - considering rates inclusive of taxes.
\ - Please either
\ - * Specify Basic Rate (i.e. Rate which will be displayed in print)
\ - -- or --
\ - * Uncheck 'Is this Tax included in Basic Rate?' in the tax entries of Taxes section."); - } - } -} - -// ************************ EXPORT RATE ************************* -cur_frm.cscript.export_rate = function(doc,cdt,cdn) { - var cur_rec = locals[cdt][cdn]; - var fname = cur_frm.cscript.fname; - var tname = cur_frm.cscript.tname; - if(flt(cur_rec.ref_rate)>0 && flt(cur_rec.export_rate)>0) { - var adj_rate = 100 * (1 - (flt(cur_rec.export_rate) / flt(cur_rec.ref_rate))); - set_multiple(tname, cur_rec.name, { 'adj_rate': adj_rate }, fname); - } - doc = locals[doc.doctype][doc.name]; - cur_frm.cscript.recalc(doc, 1); -} - - - // ************* GET OTHER CHARGES BASED ON COMPANY ************* cur_frm.fields_dict.charge.get_query = function(doc) { return 'SELECT DISTINCT `tabSales Taxes and Charges Master`.name FROM \ @@ -430,492 +580,4 @@ cur_frm.fields_dict.charge.get_query = function(doc) { ORDER BY `tabSales Taxes and Charges Master`.name LIMIT 50'; } -// ********************* Get Charges **************************** -cur_frm.cscript.get_charges = function(doc, cdt, cdn, callback) { - $c_obj(make_doclist(doc.doctype,doc.name), - 'get_other_charges', - '', - function(r, rt) { - cur_frm.cscript.calculate_charges(doc, cdt, cdn); - if(callback) callback(doc, cdt, cdn); - }, null,null,cur_frm.fields_dict.get_charges.input); -} - - -// CALCULATION OF TOTAL AMOUNTS -// ======================================================================================================== -cur_frm.cscript.recalc = function(doc, n) { - if(!n)n=0; - doc = locals[doc.doctype][doc.name]; - var tname = cur_frm.cscript.tname; - var fname = cur_frm.cscript.fname; - var sales_team = cur_frm.cscript.sales_team_fname; - var other_fname = cur_frm.cscript.other_fname; - - if(!flt(doc.conversion_rate)) { - doc.conversion_rate = 1; - refresh_field('conversion_rate'); - } - if(!flt(doc.plc_conversion_rate)) { - doc.plc_conversion_rate = 1; - refresh_field('plc_conversion_rate'); - } - - if(n > 0) cur_frm.cscript.update_fname_table(doc , tname , fname , n, other_fname); // updates all values in table (i.e. amount, export amount, net total etc.) - - if(flt(doc.net_total) > 0) { - var cl = getchildren('Sales Taxes and Charges', doc.name, other_fname,doc.doctype); - for(var i = 0; i1) { - net_total_incl *= flt(doc.conversion_rate); - } - - doc.net_total = inclusive_rate ? flt(net_total_incl) : flt(net_total); - doc.other_charges_total = roundNumber(flt(other_charges_total), 2); - doc.grand_total = roundNumber((flt(net_total) + flt(other_charges_total)), 2); - doc.rounded_total = Math.round(doc.grand_total); - doc.grand_total_export = roundNumber((flt(doc.grand_total) / flt(doc.conversion_rate)), 2); - doc.rounded_total_export = Math.round(doc.grand_total_export); - doc.total_commission = flt(flt(net_total) * flt(doc.commission_rate) / 100); -} - -// ******************************* OTHER CHARGES ************************************* -cur_frm.cscript.calc_other_charges = function(doc , tname , fname , other_fname) { - doc = locals[doc.doctype][doc.name]; - - // Make Display Area - cur_frm.fields_dict['other_charges_calculation'].disp_area.innerHTML = - 'Calculation Details for Taxes and Charges:'; - - var cl = getchildren(tname, doc.name, fname); - var tax = getchildren('Sales Taxes and Charges', doc.name, other_fname,doc.doctype); - - // Make display table - var otc = make_table(cur_frm.fields_dict['other_charges_calculation'].disp_area, - cl.length + 1, tax.length + 1, '90%', [], { border:'1px solid #AAA', padding:'2px' }); - $y(otc,{marginTop:'8px'}); - - var tax_desc = {}; var tax_desc_rates = []; var net_total = 0; - - for(var i=0;i2) alert("You cannot enter more than 2 nos. for division"); - var id1 = cint(row[0].replace(/^\s+|\s+$/g,"")); - var id2 = cint(row[1].replace(/^\s+|\s+$/g,"")); - tax_amount = flt(tax[id1-1].total_amount) / flt(tax[id2-1].total_amount); - } - return tax_amount - } - else if(tax[t].charge_type == 'On Previous Row Total') { - if(flt(print_amt) == 1) { - doc.sales_tax_rate += flt(rate); - refresh_field('sales_tax_rate'); - return - } - var row = cint(tax[t].row_id); - return tax_amount = flt(rate) * (flt(tax[row-1].total_tax_amount)+flt(tax[row-1].total_amount)) / 100; - } -} - -// ********************** Functions for inclusive value calc ****************************** -cur_frm.cscript.consider_incl_rate = function(doc, other_fname) { - var tax_list = getchildren('Sales Taxes and Charges', doc.name, other_fname, doc.doctype); - for(var i=0; i \ - * On Net Total
\ - * On Previous Row Amount
\ - * On Previous Row Total"); - tax.included_in_print_rate = 0; - refresh_field('included_in_print_rate', tax.name, cur_frm.cscript.other_fname); - } - var tax_list = getchildren('Sales Taxes and Charges', doc.name, cur_frm.cscript.other_fname, doc.doctype); - cur_frm.cscript.validate_print_rate_option(doc, tax_list, tax.idx-1); - } -} - -// ********************** Update values in table ****************************** -cur_frm.cscript.update_fname_table = function(doc , tname , fname , n, other_fname) { - doc = locals[doc.doctype][doc.name] - var net_total = 0 - var cl = getchildren(tname, doc.name, fname); - var consider_incl_rate = cur_frm.cscript.consider_incl_rate(doc, other_fname); - for(var i=0;i 0) { - set_multiple(tname, cl[i].name, { - 'export_rate': flt(flt(cl[i].ref_rate) * (100 - flt(cl[i].adj_rate)) / 100) - }, fname); - } - set_multiple(tname, cl[i].name, { - 'export_amount': flt(flt(cl[i].qty) * flt(cl[i].export_rate)), - 'basic_rate': flt(flt(cl[i].export_rate) * flt(doc.conversion_rate)), - 'amount': roundNumber(flt((flt(cl[i].export_rate) * flt(doc.conversion_rate)) * flt(cl[i].qty)), 2) - }, fname); - //var base_ref_rate = flt(cl[i].basic_rate) + flt(flt(cl[i].basic_rate) * flt(cl[i].adj_rate) / 100); - //set_multiple(tname, cl[i].name, { - // 'base_ref_rate': flt(base_ref_rate) - //}, fname); - - } else if(consider_incl_rate) { - if(flt(cl[i].export_rate) > 0) { - // calculate basic rate based on taxes - // then calculate and set basic_rate, base_ref_rate, ref_rate, amount, export_amount - var ref_rate = flt(cl[i].adj_rate)!=flt(100) ? - flt((100 * flt(cl[i].export_rate))/flt(100 - flt(cl[i].adj_rate))) : - flt(0) - set_multiple(tname, cl[i].name, { 'ref_rate': ref_rate }, fname); - } else if((flt(cl[i].ref_rate) > 0) && (flt(cl[i].adj_rate) > 0)) { - var export_rate = flt(cl[i].ref_rate) * flt(1 - flt(cl[i].adj_rate / 100)); - set_multiple(tname, cl[i].name, { 'export_rate': flt(export_rate) }, fname); - } - //console.log("export_rate: " + cl[i].export_rate); - - var basic_rate = cur_frm.cscript.back_calc_basic_rate(doc, tname, fname, cl[i], other_fname); - var base_ref_rate = basic_rate + flt(basic_rate * flt(cl[i].adj_rate) / 100); - set_multiple(tname, cl[i].name, { - 'basic_rate': flt(basic_rate), - 'amount': roundNumber(flt(basic_rate * flt(cl[i].qty)), 2), - 'export_amount': flt(flt(cl[i].qty) * flt(cl[i].export_rate)), - 'base_ref_rate': flt(base_ref_rate) - }, fname); - } - } - else if(n == 2){ - if(flt(cl[i].ref_rate) > 0) - set_multiple(tname, cl[i].name, {'adj_rate': 100 - flt(flt(cl[i].basic_rate) * 100 / (flt(cl[i].ref_rate) * flt(doc.conversion_rate)))}, fname); - set_multiple(tname, cl[i].name, {'amount': flt(flt(cl[i].qty) * flt(cl[i].basic_rate)), 'export_rate': flt(flt(cl[i].basic_rate) / flt(doc.conversion_rate)), 'export_amount': flt((flt(cl[i].basic_rate) / flt(doc.conversion_rate)) * flt(cl[i].qty)) }, fname); - } - /*else if(n == 3){ - set_multiple(tname, cl[i].name, {'basic_rate': flt(flt(cl[i].export_rate) * flt(doc.conversion_rate))}, fname); - set_multiple(tname, cl[i].name, {'amount' : flt(flt(cl[i].basic_rate) * flt(cl[i].qty)), 'export_amount': flt(flt(cl[i].export_rate) * flt(cl[i].qty))}, fname); - if(cl[i].ref_rate > 0) - set_multiple(tname, cl[i].name, {'adj_rate': 100 - flt(flt(cl[i].export_rate) * 100 / flt(cl[i].ref_rate)), 'base_ref_rate': flt(flt(cl[i].ref_rate) * flt(doc.conversion_rate)) }, fname); - }*/ - net_total += flt(flt(cl[i].qty) * flt(cl[i].basic_rate)); - } - doc.net_total = net_total; - refresh_field('net_total'); -} - -cur_frm.cscript.get_item_wise_tax_detail = function( doc, rate, cl, i, tax, t) { - doc = locals[doc.doctype][doc.name]; - var detail = ''; - detail = cl[i].item_code + " : " + cstr(rate) + NEWLINE; - return detail; -} - -// **************** RE-CALCULATE VALUES *************************** - -cur_frm.cscript.recalculate_values = function(doc, cdt, cdn) { - cur_frm.cscript.calculate_charges(doc,cdt,cdn); -} - -cur_frm.cscript.validate_print_rate_option = function(doc, taxes, i) { - if(in_list(['On Previous Row Amount','On Previous Row Total'], taxes[i].charge_type)) { - if(!taxes[i].row_id){ - alert("Please Enter Row on which amount needs to be calculated for row : "+taxes[i].idx); - validated = false; - } else if(taxes[i].included_in_print_rate && taxes[taxes[i].row_id-1].charge_type=='Actual') { - msgprint("Row of type 'Actual' cannot be depended on for type '" + taxes[i].charge_type + "'\ - when using tax inclusive prices.
\ - This will lead to incorrect values.

\ - Please specify correct value in 'Enter Row' column of Row: " - + taxes[i].idx + " in Taxes table"); - validated = false; - taxes[i].included_in_print_rate = 0; - refresh_field('included_in_print_rate', taxes[i].name, other_fname); - } else if ((taxes[i].included_in_print_rate && !taxes[taxes[i].row_id-1].included_in_print_rate) || - (!taxes[i].included_in_print_rate && taxes[taxes[i].row_id-1].included_in_print_rate)) { - msgprint("If any row in the tax table depends on 'Previous Row Amount/Total',
\ - 'Is this Tax included in Basic Rate?' column should be same for both row
\ - i.e for that row and the previous row.

\ - The same is violated for row #"+(i+1)+" and row #"+taxes[i].row_id - ); - validated = false; - } - } -} - -cur_frm.cscript.calculate_charges = function(doc, cdt, cdn) { - var other_fname = cur_frm.cscript.other_fname; - - var cl = getchildren('Sales Taxes and Charges', doc.name, other_fname, doc.doctype); - for(var i = 0; i 100){ - alert("Commision rate cannot be greater than 100."); - doc.total_commission = 0; - doc.commission_rate = 0; - } else { - doc.total_commission = doc.net_total * doc.commission_rate / 100; - } - refresh_many(['total_commission','commission_rate']); - -} - -// *******Total Commission Trigger (calculates commission rate)********* -cur_frm.cscript.total_commission = function(doc, cdt, cdn) { - if(doc.net_total){ - if(doc.net_total < doc.total_commission){ - alert("Total commission cannot be greater than net total."); - doc.total_commission = 0; - doc.commission_rate = 0; - } else { - doc.commission_rate = doc.total_commission * 100 / doc.net_total; - } - refresh_many(['total_commission','commission_rate']); - } -} -// Sales Person Allocated % trigger -// ============================================================================== -cur_frm.cscript.allocated_percentage = function(doc, cdt, cdn) { - var fname = cur_frm.cscript.sales_team_fname; - var d = locals[cdt][cdn]; - if (d.allocated_percentage) { - d.allocated_amount = flt(flt(doc.net_total)*flt(d.allocated_percentage)/100); - refresh_field('allocated_amount', d.name, fname); - } -} - -// Client Side Validation -// ================================================================================= -cur_frm.cscript.validate = function(doc, cdt, cdn) { - cur_frm.cscript.validate_items(doc); - var cl = getchildren('Sales Taxes and Charges Master', doc.name, 'other_charges'); - for(var i =0;i now() - or end_of_life = '0000-00-00') and (is_sales_item = 'Yes' - or is_service_item = 'Yes')""", args['item_code'], as_dict=1) - tax = webnotes.conn.sql("""select tax_type, tax_rate from `tabItem Tax` - where parent = %s""", args['item_code']) - t = {} - for x in tax: t[x[0]] = flt(x[1]) - ret = { - 'description': item and item[0]['description_html'] or \ - item[0]['description'], - 'barcode': item and item[0]['barcode'] or '', - 'item_group': item and item[0]['item_group'] or '', - 'item_name': item and item[0]['item_name'] or '', - 'brand': item and item[0]['brand'] or '', - 'stock_uom': item and item[0]['stock_uom'] or '', - 'reserved_warehouse': item and item[0]['default_warehouse'] or '', - 'warehouse': item and item[0]['default_warehouse'] or \ - args.get('warehouse'), - 'income_account': item and item[0]['default_income_account'] or \ - args.get('income_account'), - 'expense_account': item and item[0]['purchase_account'] or \ - args.get('expense_account'), - 'cost_center': item and item[0]['default_sales_cost_center'] or \ - args.get('cost_center'), - # this is done coz if item once fetched is fetched again than its qty shld be reset to 1 - 'qty': 1.00, - 'adj_rate': 0, - 'amount': 0, - 'export_amount': 0, - 'item_tax_rate': json.dumps(t), - 'batch_no': '' - } - if(obj.doc.price_list_name and item): #this is done to fetch the changed BASIC RATE and REF RATE based on PRICE LIST - base_ref_rate = self.get_ref_rate(args['item_code'], obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate) - ret['ref_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - ret['export_rate'] = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - ret['base_ref_rate'] = flt(base_ref_rate) - ret['basic_rate'] = flt(base_ref_rate) - - if ret['warehouse'] or ret['reserved_warehouse']: - av_qty = self.get_available_qty({'item_code': args['item_code'], 'warehouse': ret['warehouse'] or ret['reserved_warehouse']}) - ret.update(av_qty) - - # get customer code for given item from Item Customer Detail - customer_item_code_row = webnotes.conn.sql("""\ - select ref_code from `tabItem Customer Detail` - where parent = %s and customer_name = %s""", - (args['item_code'], obj.doc.customer)) - if customer_item_code_row and customer_item_code_row[0][0]: - ret['customer_item_code'] = customer_item_code_row[0][0] - - return ret - - - def get_item_defaults(self, args): - item = webnotes.conn.sql("""select default_warehouse, default_income_account, - default_sales_cost_center, purchase_account from `tabItem` where name = %s - and (ifnull(end_of_life,'') = '' or end_of_life > now() or end_of_life = '0000-00-00') - and (is_sales_item = 'Yes' or is_service_item = 'Yes') """, - (args['item_code']), as_dict=1) - ret = { - 'reserved_warehouse': item and item[0]['default_warehouse'] or '', - 'warehouse': item and item[0]['default_warehouse'] or args.get('warehouse'), - 'income_account': item and item[0]['default_income_account'] or \ - args.get('income_account'), - 'expense_account': item and item[0]['purchase_account'] or args.get('expense_account'), - 'cost_center': item and item[0]['default_sales_cost_center'] or args.get('cost_center'), - } - - return ret - - def get_available_qty(self,args): - tot_avail_qty = webnotes.conn.sql("select projected_qty, actual_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (args['item_code'], args['warehouse']), as_dict=1) - ret = { - 'projected_qty' : tot_avail_qty and flt(tot_avail_qty[0]['projected_qty']) or 0, - 'actual_qty' : tot_avail_qty and flt(tot_avail_qty[0]['actual_qty']) or 0 - } - return ret - - - # ***************** Get Ref rate as entered in Item Master ******************** - def get_ref_rate(self, item_code, price_list_name, price_list_currency, plc_conv_rate): - ref_rate = webnotes.conn.sql("select ref_rate from `tabItem Price` where parent = %s and price_list_name = %s and ref_currency = %s and selling=1", - (item_code, price_list_name, price_list_currency)) - base_ref_rate = ref_rate and flt(ref_rate[0][0]) * flt(plc_conv_rate) or 0 - return base_ref_rate - - def get_barcode_details(self, barcode): - item = webnotes.conn.sql("select name, end_of_life, is_sales_item, is_service_item \ - from `tabItem` where barcode = %s", barcode, as_dict=1) - ret = {} - if not item: - msgprint("""No item found for this barcode: %s. - May be barcode not updated in item master. Please check""" % barcode) - elif item[0]['end_of_life'] and getdate(cstr(item[0]['end_of_life'])) < nowdate(): - msgprint("Item: %s has been expired. Please check 'End of Life' field in item master" % item[0]['name']) - elif item[0]['is_sales_item'] == 'No' and item[0]['is_service_item'] == 'No': - msgprint("Item: %s is not a sales or service item" % item[0]['name']) - elif len(item) > 1: - msgprint("There are multiple item for this barcode. \nPlease select item code manually") - else: - ret = {'item_code': item and item[0]['name'] or ''} - - return ret - - - # ****** Re-cancellculates Basic Rate & amount based on Price List Selected ****** - def get_adj_percent(self, obj): - for d in getlist(obj.doclist, obj.fname): - base_ref_rate = self.get_ref_rate(d.item_code, obj.doc.price_list_name, obj.doc.price_list_currency, obj.doc.plc_conversion_rate) - d.adj_rate = 0 - d.ref_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - d.basic_rate = flt(base_ref_rate) - d.base_ref_rate = flt(base_ref_rate) - d.export_rate = flt(base_ref_rate)/flt(obj.doc.conversion_rate) - d.amount = flt(d.qty)*flt(base_ref_rate) - d.export_amount = flt(d.qty)*flt(base_ref_rate)/flt(obj.doc.conversion_rate) - - - # Load Default Taxes - # ==================== - def load_default_taxes(self, obj): - if cstr(obj.doc.charge): - return self.get_other_charges(obj) - else: - return self.get_other_charges(obj, 1) - - - # Get other charges from Master - # ================================================================================= - def get_other_charges(self,obj, default=0): - obj.doclist = obj.doc.clear_table(obj.doclist, 'other_charges') - if not getlist(obj.doclist, 'other_charges'): - if default: add_cond = 'ifnull(t2.is_default,0) = 1' - else: add_cond = 't1.parent = "'+cstr(obj.doc.charge)+'"' - idx = 0 - other_charge = webnotes.conn.sql("""\ - select t1.* - from - `tabSales Taxes and Charges` t1, - `tabSales Taxes and Charges Master` t2 - where - t1.parent = t2.name and - t2.company = '%s' and - %s - order by t1.idx""" % (obj.doc.company, add_cond), as_dict=1) - from webnotes.model import default_fields - for other in other_charge: - # remove default fields like parent, parenttype etc. - # from query results - for field in default_fields: - if field in other: del other[field] - - d = addchild(obj.doc, 'other_charges', 'Sales Taxes and Charges', - obj.doclist) - d.fields.update(other) - d.rate = flt(d.rate) - d.tax_amount = flt(d.tax_rate) - d.included_in_print_rate = cint(d.included_in_print_rate) - d.idx = idx - idx += 1 - return obj.doclist # Get TERMS AND CONDITIONS # ======================================================================================= @@ -327,23 +116,6 @@ class DocType(TransactionBase): } return ret - # Get Commission rate - # ======================================================================= - def get_comm_rate(self, sales_partner, obj): - - comm_rate = webnotes.conn.sql("select commission_rate from `tabSales Partner` where name = '%s' and docstatus != 2" %(sales_partner), as_dict=1) - if comm_rate: - total_comm = flt(comm_rate[0]['commission_rate']) * flt(obj.doc.net_total) / 100 - ret = { - 'commission_rate' : comm_rate and flt(comm_rate[0]['commission_rate']) or 0, - 'total_commission' : flt(total_comm) - } - return ret - else: - msgprint("Business Associate : %s does not exist in the system." % (sales_partner)) - raise Exception - - # To verify whether rate entered in details table does not exceed max discount % # ======================================================================================= def validate_max_discount(self,obj, detail_table): @@ -352,16 +124,6 @@ class DocType(TransactionBase): if discount and discount[0]['max_discount'] and (flt(d.adj_rate)>flt(discount[0]['max_discount'])): msgprint("You cannot give more than " + cstr(discount[0]['max_discount']) + " % discount on Item Code : "+cstr(d.item_code)) raise Exception - - - # Get sum of allocated % of sales person (it should be 100%) - # ======================================================================== - # it indicates % contribution of sales person in sales - def get_allocated_sum(self,obj): - sales_team_list = obj.doclist.get({"parentfield": "sales_team"}) - total_allocation = sum([flt(d.allocated_percentage) for d in sales_team_list]) - if sales_team_list and total_allocation != 100.0: - msgprint("Total Allocated % of Sales Persons should be 100%", raise_exception=True) # Check Conversion Rate (i.e. it will not allow conversion rate to be 1 for Currency other than default currency set in Global Defaults) # =========================================================================== diff --git a/selling/doctype/sales_order/sales_order.js b/selling/doctype/sales_order/sales_order.js index b7927543847..ded9c447e81 100644 --- a/selling/doctype/sales_order/sales_order.js +++ b/selling/doctype/sales_order/sales_order.js @@ -26,105 +26,54 @@ wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); - -cur_frm.cscript.onload = function(doc, cdt, cdn) { - cur_frm.cscript.manage_rounded_total(); - - if(!doc.status) set_multiple(cdt,cdn,{status:'Draft'}); - if(!doc.transaction_date) set_multiple(cdt,cdn,{transaction_date:get_today()}); - if(!doc.price_list_currency) set_multiple(cdt, cdn, {price_list_currency: doc.currency, plc_conversion_rate: 1}); - // load default charges - - if(doc.__islocal && !doc.customer){ - hide_field(['customer_address','contact_person', 'customer_name', - 'address_display', 'contact_display', 'contact_mobile', - 'contact_email', 'territory', 'customer_group']); - } -} - -cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { - var callback = function(doc, cdt, cdn) { - if(doc.__islocal) { - // defined in sales_common.js - cur_frm.cscript.update_item_details(doc, cdt, cdn); - } - } - - cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn, callback); - -} - - -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - cur_frm.clear_custom_buttons(); - erpnext.hide_naming_series(); - - if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn); - - cur_frm.toggle_display("contact_info", doc.customer); - - if(doc.docstatus==1) { - if(doc.status != 'Stopped') { - cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); - // delivery note - if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales') - cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']); +erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({ + refresh: function(doc, dt, dn) { + this._super(); + + if(doc.docstatus==1) { + if(doc.status != 'Stopped') { + cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + // delivery note + if(flt(doc.per_delivered, 2) < 100 && doc.order_type=='Sales') + cur_frm.add_custom_button('Make Delivery', cur_frm.cscript['Make Delivery Note']); - // maintenance - if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) { - cur_frm.add_custom_button('Make Maint. Visit', cur_frm.cscript.make_maintenance_visit); - cur_frm.add_custom_button('Make Maint. Schedule', cur_frm.cscript['Make Maintenance Schedule']); + // maintenance + if(flt(doc.per_delivered, 2) < 100 && (doc.order_type !='Sales')) { + cur_frm.add_custom_button('Make Maint. Visit', cur_frm.cscript.make_maintenance_visit); + cur_frm.add_custom_button('Make Maint. Schedule', cur_frm.cscript['Make Maintenance Schedule']); + } + + // indent + if(!doc.order_type || (doc.order_type == 'Sales')) + cur_frm.add_custom_button('Make ' + wn._('Material Request'), cur_frm.cscript['Make Material Request']); + + // sales invoice + if(flt(doc.per_billed, 2) < 100) + cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']); + + // stop + if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100) + cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']); + } else { + // un-stop + cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']); } - - // indent - if(!doc.order_type || (doc.order_type == 'Sales')) - cur_frm.add_custom_button('Make ' + wn._('Material Request'), cur_frm.cscript['Make Material Request']); - - // sales invoice - if(flt(doc.per_billed, 2) < 100) - cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']); - - // stop - if(flt(doc.per_delivered, 2) < 100 || doc.per_billed < 100) - cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Sales Order']); - } else { - // un-stop - cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Sales Order']); } - } - cur_frm.cscript.order_type(doc); -} - -cur_frm.cscript.order_type = function(doc) { - if(doc.order_type == "Sales") { - cur_frm.toggle_reqd("delivery_date", 1); - } else { - cur_frm.toggle_reqd("delivery_date", 0); - } -} - -//customer -cur_frm.cscript.customer = function(doc,dt,dn) { - cur_frm.toggle_display("contact_info", doc.customer); + this.order_type(doc); + }, - var pl = doc.price_list_name; - var callback = function(r,rt) { - var callback2 = function(r, rt) { - if(doc.customer) - unhide_field(['customer_address', 'contact_person', 'territory','customer_group']); - cur_frm.refresh(); - - if(!onload && (pl != doc.price_list_name)) cur_frm.cscript.price_list_name(doc, dt, dn); + order_type: function() { + this.frm.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales"); + }, + + reserved_warehouse: function(doc, cdt, cdn) { + this.warehouse(doc, cdt, cdn); + }, +}); - } - var doc = locals[cur_frm.doctype][cur_frm.docname]; - get_server_fields('get_shipping_address',doc.customer,'',doc, dt, dn, 0, callback2); - - } - if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), - 'get_default_customer_address', '', callback); -} +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, new erpnext.selling.SalesOrderController({frm: cur_frm})); cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); @@ -180,15 +129,6 @@ cur_frm.fields_dict['quotation_no'].get_query = function(doc) { ORDER BY `tabQuotation`.`name` DESC LIMIT 50', {cond:cond}); } - -cur_frm.cscript.reserved_warehouse = function(doc, cdt , cdn) { - var d = locals[cdt][cdn]; - if (d.reserved_warehouse) { - arg = "{'item_code':'" + d.item_code + "','warehouse':'" + d.reserved_warehouse +"'}"; - get_server_fields('get_available_qty',arg,'sales_order_details',doc,cdt,cdn,1); - } -} - //----------- make maintenance schedule---------- cur_frm.cscript['Make Maintenance Schedule'] = function() { var doc = cur_frm.doc; diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py index c8db1f919f1..79b37be0e52 100644 --- a/selling/doctype/sales_order/sales_order.py +++ b/selling/doctype/sales_order/sales_order.py @@ -58,22 +58,6 @@ class DocType(SellingController): def get_comm_rate(self, sales_partner): return get_obj('Sales Common').get_comm_rate(sales_partner, self) - def get_item_details(self, args=None): - import json - args = args and json.loads(args) or {} - if args.get('item_code'): - return get_obj('Sales Common').get_item_details(args, self) - else: - obj = get_obj('Sales Common') - for doc in self.doclist: - if doc.fields.get('item_code'): - arg = {'item_code':doc.fields.get('item_code'), 'income_account':doc.fields.get('income_account'), - 'cost_center': doc.fields.get('cost_center'), 'warehouse': doc.fields.get('warehouse')}; - ret = obj.get_item_defaults(arg) - for r in ret: - if not doc.fields.get(r): - doc.fields[r] = ret[r] - def get_adj_percent(self, arg=''): get_obj('Sales Common').get_adj_percent(self) @@ -83,12 +67,6 @@ class DocType(SellingController): def get_rate(self,arg): return get_obj('Sales Common').get_rate(arg) - def load_default_taxes(self): - self.doclist = get_obj('Sales Common').load_default_taxes(self) - - def get_other_charges(self): - self.doclist = get_obj('Sales Common').get_other_charges(self) - def get_tc_details(self): return get_obj('Sales Common').get_tc_details(self) @@ -194,6 +172,8 @@ class DocType(SellingController): and current Sales Order""" % (self.doc.order_type, d.prevdoc_docname)) def validate_order_type(self): + super(DocType, self).validate_order_type() + #validate delivery date if self.doc.order_type == 'Sales' and not self.doc.delivery_date: msgprint("Please enter 'Expected Delivery Date'") @@ -226,7 +206,6 @@ class DocType(SellingController): sales_com_obj.check_conversion_rate(self) sales_com_obj.validate_max_discount(self,'sales_order_details') - sales_com_obj.get_allocated_sum(self) self.doclist = sales_com_obj.make_packing_list(self,'sales_order_details') if not self.doc.status: diff --git a/selling/doctype/sales_order/sales_order.txt b/selling/doctype/sales_order/sales_order.txt index ba0b1de07c8..fb5848f8c36 100644 --- a/selling/doctype/sales_order/sales_order.txt +++ b/selling/doctype/sales_order/sales_order.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-07 14:48:34", + "creation": "2013-05-24 19:29:08", "docstatus": 0, - "modified": "2013-01-29 17:14:58", + "modified": "2013-05-28 15:05:38", "modified_by": "Administrator", "owner": "Administrator" }, @@ -76,42 +76,47 @@ "search_index": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "label": "Name", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -230,7 +235,8 @@ "oldfieldname": "sales_order_details", "oldfieldtype": "Table", "options": "Sales Order Item", - "print_hide": 0 + "print_hide": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -251,11 +257,19 @@ "oldfieldname": "net_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 0, + "print_hide": 1, "read_only": 1, "reqd": 0, "width": "150px" }, + { + "doctype": "DocField", + "fieldname": "net_total_export", + "fieldtype": "Currency", + "label": "Net Total (Export)", + "options": "currency", + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "recalculate_values", @@ -426,7 +440,7 @@ "doctype": "DocField", "fieldname": "other_charges_total", "fieldtype": "Currency", - "label": "Taxes and Charges Total*", + "label": "Taxes and Charges Total", "oldfieldname": "other_charges_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -434,6 +448,15 @@ "read_only": 1, "width": "150px" }, + { + "doctype": "DocField", + "fieldname": "other_charges_total_export", + "fieldtype": "Currency", + "label": "Taxes and Charges Total (Export)", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "other_charges_calculation", @@ -578,6 +601,7 @@ "print_hide": 0 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_info", "fieldtype": "Section Break", @@ -955,7 +979,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Sales Manager", @@ -978,7 +1001,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "permlevel": 0, "report": 1, "role": "Sales User", @@ -990,7 +1012,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Sales User", @@ -1013,7 +1034,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "Maintenance Manager", "submit": 0 @@ -1034,7 +1054,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "Maintenance User", "submit": 0 diff --git a/selling/doctype/sales_order_item/sales_order_item.txt b/selling/doctype/sales_order_item/sales_order_item.txt index c65ac0d9385..7276fc329c0 100644 --- a/selling/doctype/sales_order_item/sales_order_item.txt +++ b/selling/doctype/sales_order_item/sales_order_item.txt @@ -2,7 +2,7 @@ { "creation": "2013-03-07 11:42:58", "docstatus": 0, - "modified": "2013-05-22 12:09:03", + "modified": "2013-05-22 12:10:03", "modified_by": "Administrator", "owner": "Administrator" }, @@ -113,7 +113,7 @@ "options": "currency", "print_hide": 1, "print_width": "70px", - "read_only": 0, + "read_only": 1, "reqd": 0, "width": "70px" }, @@ -183,7 +183,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "reqd": 0, "width": "100px" }, diff --git a/selling/doctype/sales_team/sales_team.txt b/selling/doctype/sales_team/sales_team.txt index add466c334c..29a951eef70 100644 --- a/selling/doctype/sales_team/sales_team.txt +++ b/selling/doctype/sales_team/sales_team.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:53", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-03-07 07:03:31", + "modified": "2013-05-21 17:04:45", "modified_by": "Administrator", "owner": "Administrator" }, @@ -42,6 +42,7 @@ "doctype": "DocField", "fieldname": "sales_designation", "fieldtype": "Data", + "hidden": 0, "label": "Designation", "oldfieldname": "sales_designation", "oldfieldtype": "Data", @@ -63,7 +64,7 @@ "doctype": "DocField", "fieldname": "allocated_percentage", "fieldtype": "Float", - "label": "Allocated (%)", + "label": "Contribution (%)", "oldfieldname": "allocated_percentage", "oldfieldtype": "Currency", "print_width": "100px", @@ -74,11 +75,12 @@ "doctype": "DocField", "fieldname": "allocated_amount", "fieldtype": "Currency", - "label": "Allocated Amount", + "label": "Contribution to Net Total", "oldfieldname": "allocated_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_width": "120px", + "read_only": 1, "reqd": 0, "width": "120px" }, diff --git a/selling/utils.py b/selling/utils.py index 21e94f72348..6901028710c 100644 --- a/selling/utils.py +++ b/selling/utils.py @@ -16,6 +16,9 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ +from webnotes.utils import flt, cint, comma_and +import json def get_customer_list(doctype, txt, searchfield, start, page_len, filters): if webnotes.conn.get_default("cust_master_name") == "Customer Name": @@ -29,4 +32,149 @@ def get_customer_list(doctype, txt, searchfield, start, page_len, filters): case when customer_name like %s then 0 else 1 end, name, customer_name limit %s, %s""" % (", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"), - ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) \ No newline at end of file + ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len)) + +@webnotes.whitelist() +def get_item_details(args): + """ + args = { + "item_code": "", + "warehouse": None, + "customer": "", + "conversion_rate": 1.0, + "price_list_name": None, + "price_list_currency": None, + "plc_conversion_rate": 1.0 + } + """ + if isinstance(args, basestring): + args = json.loads(args) + args = webnotes._dict(args) + + if args.barcode: + args.item_code = _get_item_code(args.barcode) + + item_bean = webnotes.bean("Item", args.item_code) + + _validate_item_details(args, item_bean.doc) + + out = _get_basic_details(args, item_bean) + + meta = webnotes.get_doctype(args.doctype) + if meta.get_field("currency"): + out.base_ref_rate = out.basic_rate = out.ref_rate = out.export_rate = 0.0 + + if args.price_list_name and args.price_list_currency: + out.update(_get_price_list_rate(args, item_bean, meta)) + + if out.warehouse or out.reserved_warehouse: + out.update(get_available_qty(args.item_code, out.warehouse or out.reserved_warehouse)) + + out.customer_item_code = _get_customer_item_code(args, item_bean) + + if cint(args.is_pos): + pos_settings = get_pos_settings(args.company) + out.update(apply_pos_settings(pos_settings, out)) + + return out + +def _get_item_code(barcode): + item_code = webnotes.conn.sql_list("""select name from `tabItem` where barcode=%s""", barcode) + + if not item_code: + msgprint(_("No Item found with Barcode") + ": %s" % barcode, raise_exception=True) + + elif len(item_code) > 1: + msgprint(_("Items") + " %s " % comma_and(item_code) + + _("have the same Barcode") + " %s" % barcode, raise_exception=True) + + return item_code[0] + +def _validate_item_details(args, item): + from utilities.transaction_base import validate_item_fetch + validate_item_fetch(args, item) + + # validate if sales item or service item + if args.order_type == "Maintenance": + if item.is_service_item != "Yes": + msgprint(_("Item") + (" %s: " % item.name) + + _("not a service item.") + + _("Please select a service item or change the order type to Sales."), + raise_exception=True) + + elif item.is_sales_item != "Yes": + msgprint(_("Item") + (" %s: " % item.name) + _("not a sales item"), + raise_exception=True) + +def _get_basic_details(args, item_bean): + item = item_bean.doc + out = webnotes._dict({ + "item_code": item.name, + "description": item.description_html or item.description, + "reserved_warehouse": item.default_warehouse, + "warehouse": item.default_warehouse or args.warehouse, + "income_account": item.default_income_account or args.income_account, + "expense_account": item.purchase_account or args.expense_account, + "cost_center": item.default_sales_cost_center or args.cost_center, + "qty": 1.0, + "adj_rate": 0.0, + "export_amount": 0.0, + "amount": 0.0, + "batch_no": None, + "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in + item_bean.doclist.get({"parentfield": "item_tax"})))), + }) + + for fieldname in ("item_name", "item_group", "barcode", "brand", "stock_uom"): + out[fieldname] = item.fields.get(fieldname) + + return out + +def _get_price_list_rate(args, item_bean, meta): + base_ref_rate = item_bean.doclist.get({ + "parentfield": "ref_rate_details", + "price_list_name": args.price_list_name, + "price_list_currency": args.price_list_currency, + "selling": 1}) + + if not base_ref_rate: + return {} + + # found price list rate - now we can validate + from utilities.transaction_base import validate_currency + validate_currency(args, item_bean.doc, meta) + + return {"ref_rate": flt(base_ref_rate[0].ref_rate * args.plc_conversion_rate / args.conversion_rate)} + +@webnotes.whitelist() +def get_available_qty(item_code, warehouse): + return webnotes.conn.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, + ["projected_qty", "actual_qty"], as_dict=True) or {} + +def _get_customer_item_code(args, item_bean): + customer_item_code = item_bean.doclist.get({"parentfield": "item_customer_details", + "customer_name": args.customer}) + + return customer_item_code and customer_item_code[0].ref_code or None + +def get_pos_settings(company): + pos_settings = webnotes.conn.sql("""select * from `tabPOS Setting` where user = %s + and company = %s""", (webnotes.session['user'], company), as_dict=1) + + if not pos_settings: + pos_settings = webnotes.conn.sql("""select * from `tabPOS Setting` + where ifnull(user,'') = '' and company = %s""", company, as_dict=1) + + return pos_settings and pos_settings[0] or None + +def apply_pos_settings(pos_settings, opts): + out = {} + + for fieldname in ("income_account", "cost_center", "warehouse", "expense_account"): + if not opts.get(fieldname): + out[fieldname] = pos_settings.get(fieldname) + + if out.get("warehouse"): + out["actual_qty"] = get_available_qty(opts.item_code, out.get("warehouse")).get("actual_qty") + + return out diff --git a/setup/utils.py b/setup/utils.py index 1a86921692e..33fa3e286e7 100644 --- a/setup/utils.py +++ b/setup/utils.py @@ -46,4 +46,4 @@ def get_price_list_currency(args): if result and len(result)==1: return {"price_list_currency": result[0][0]} else: - return {} \ No newline at end of file + return {} diff --git a/startup/boot.py b/startup/boot.py index 9ed20ff73aa..b202d17697a 100644 --- a/startup/boot.py +++ b/startup/boot.py @@ -36,9 +36,8 @@ def boot_session(bootinfo): for key in ['max_users', 'expires_on', 'max_space', 'status', 'developer_mode']: if hasattr(conf, key): bootinfo[key] = getattr(conf, key) - bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center, - cost_center as 'cost_center_other_charges' from `tabCompany`""", - as_dict=1, update={"doctype":":Company"}) + bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center + from `tabCompany`""", as_dict=1, update={"doctype":":Company"}) def get_letter_heads(): """load letter heads with startup""" diff --git a/stock/Print Format/Delivery Note Classic/Delivery Note Classic.txt b/stock/Print Format/Delivery Note Classic/Delivery Note Classic.txt index c5beb65b0de..4f6272d7732 100644 --- a/stock/Print Format/Delivery Note Classic/Delivery Note Classic.txt +++ b/stock/Print Format/Delivery Note Classic/Delivery Note Classic.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:31:11", "docstatus": 0, - "modified": "2013-01-25 17:19:46", + "modified": "2013-05-28 17:20:31", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Delivery Note", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Stock", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/stock/Print Format/Delivery Note Modern/Delivery Note Modern.txt b/stock/Print Format/Delivery Note Modern/Delivery Note Modern.txt index 6993bfdce01..82d2376d044 100644 --- a/stock/Print Format/Delivery Note Modern/Delivery Note Modern.txt +++ b/stock/Print Format/Delivery Note Modern/Delivery Note Modern.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:31:11", "docstatus": 0, - "modified": "2013-01-25 17:20:58", + "modified": "2013-05-28 17:20:21", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Delivery Note", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Stock", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/stock/Print Format/Delivery Note Spartan/Delivery Note Spartan.txt b/stock/Print Format/Delivery Note Spartan/Delivery Note Spartan.txt index 69eb9ff66fa..81ecbd29b5c 100644 --- a/stock/Print Format/Delivery Note Spartan/Delivery Note Spartan.txt +++ b/stock/Print Format/Delivery Note Spartan/Delivery Note Spartan.txt @@ -1,17 +1,18 @@ [ { - "creation": "2012-04-17 11:29:12", + "creation": "2013-04-19 13:31:11", "docstatus": 0, - "modified": "2013-01-25 17:19:30", + "modified": "2013-05-28 17:20:39", "modified_by": "Administrator", "owner": "Administrator" }, { "doc_type": "Delivery Note", "doctype": "Print Format", - "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", + "html": "\n\n\n\n\n\n\n\n\n\n\n\n
\n\t\n\t\n
\n\n", "module": "Stock", "name": "__common__", + "print_format_type": "Client", "standard": "Yes" }, { diff --git a/stock/doctype/delivery_note/delivery_note.js b/stock/doctype/delivery_note/delivery_note.js index 0a31dfe80e4..e1b23f0c72f 100644 --- a/stock/doctype/delivery_note/delivery_note.js +++ b/stock/doctype/delivery_note/delivery_note.js @@ -20,84 +20,36 @@ cur_frm.cscript.fname = "delivery_note_details"; cur_frm.cscript.other_fname = "other_charges"; cur_frm.cscript.sales_team_fname = "sales_team"; -wn.require('app/selling/doctype/sales_common/sales_common.js'); wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/selling/doctype/sales_common/sales_common.js'); -// ONLOAD -// ================================================================================================ -cur_frm.cscript.onload = function(doc, dt, dn) { - cur_frm.cscript.manage_rounded_total(); - if(!doc.status) set_multiple(dt,dn,{status:'Draft'}); - if(!doc.transaction_date) set_multiple(dt,dn,{transaction_date:get_today()}); - if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()}); - if(doc.__islocal && doc.customer) cur_frm.cscript.customer(doc,dt,dn,onload=true); - if(!doc.price_list_currency) { - set_multiple(dt, dn, {price_list_currency: doc.currency, plc_conversion_rate:1}); - } +wn.provide("erpnext.stock"); +erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend({ + refresh: function(doc, dt, dn) { + this._super(); - if(doc.__islocal){ - hide_field(['customer_address', 'contact_person', 'customer_name', - 'address_display', 'contact_display', 'contact_mobile', - 'contact_email', 'territory', 'customer_group']); - } -} + if(flt(doc.per_billed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']); + + if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Installation Note', cur_frm.cscript['Make Installation Note']); -cur_frm.cscript.onload_post_render = function(doc, dt, dn) { - // defined in sales_common.js - var callback = function(doc, dt, dn) { - if(doc.__islocal) cur_frm.cscript.update_item_details(doc, dt, dn); + if (doc.docstatus==1) cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); + + if(doc.docstatus==0 && !doc.__islocal) { + cur_frm.add_custom_button('Make Packing Slip', cur_frm.cscript['Make Packing Slip']); + } + + set_print_hide(doc, dt, dn); + + // unhide expense_account and cost_center is auto_inventory_accounting enabled + var aii_enabled = cint(sys_defaults.auto_inventory_accounting) + cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp("expense_account", aii_enabled); + cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp("cost_center", aii_enabled); } +}); - cur_frm.cscript.hide_price_list_currency(doc, dt, dn, callback); -} - -// REFRESH -// ================================================================================================ -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - cur_frm.clear_custom_buttons(); - erpnext.hide_naming_series(); - - if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, cdt, cdn); - - - if(flt(doc.per_billed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Invoice', cur_frm.cscript['Make Sales Invoice']); - - if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) cur_frm.add_custom_button('Make Installation Note', cur_frm.cscript['Make Installation Note']); - - if (doc.docstatus==1) cur_frm.add_custom_button('Send SMS', cur_frm.cscript.send_sms); - - if(doc.docstatus==0 && !doc.__islocal) { - cur_frm.add_custom_button('Make Packing Slip', cur_frm.cscript['Make Packing Slip']); - } - - cur_frm.toggle_display("contact_info", doc.customer); - - set_print_hide(doc, cdt, cdn); - - // unhide expense_account and cost_center is auto_inventory_accounting enabled - var aii_enabled = cint(sys_defaults.auto_inventory_accounting) - cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp("expense_account", aii_enabled); - cur_frm.fields_dict[cur_frm.cscript.fname].grid.set_column_disp("cost_center", aii_enabled); -} - - -//customer -cur_frm.cscript.customer = function(doc,dt,dn,onload) { - cur_frm.toggle_display("contact_info", doc.customer); - - var pl = doc.price_list_name; - var callback = function(r,rt) { - var doc = locals[cur_frm.doctype][cur_frm.docname]; - if(doc.customer) - unhide_field(['customer_address','contact_person','territory','customer_group']); - cur_frm.refresh(); - if(!onload && (pl != doc.price_list_name)) cur_frm.cscript.price_list_name(doc, dt, dn); - } - var args = onload ? 'onload':'' - if(doc.customer) $c_obj(make_doclist(doc.doctype, doc.name), - 'get_default_customer_shipping_address', args, callback); -} +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm})); cur_frm.cscript.customer_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.customer) get_server_fields('get_customer_address', JSON.stringify({customer: doc.customer, address: doc.customer_address, contact: doc.contact_person}),'', doc, dt, dn, 1); @@ -157,11 +109,6 @@ cur_frm.fields_dict['sales_order_no'].get_query = function(doc) { return repl('SELECT DISTINCT `tabSales Order`.`name` FROM `tabSales Order` WHERE `tabSales Order`.company = "%(company)s" and `tabSales Order`.`docstatus` = 1 and `tabSales Order`.`status` != "Stopped" and ifnull(`tabSales Order`.per_delivered,0) < 99.99 and %(cond)s `tabSales Order`.%(key)s LIKE "%s" ORDER BY `tabSales Order`.`name` DESC LIMIT 50', {company:doc.company,cond:cond}) } - -cur_frm.cscript.delivery_type = function(doc, cdt, cdn) { - if (doc.delivery_type = 'Sample') cfn_set_fields(doc, cdt, cdn); -} - cur_frm.cscript.serial_no = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; if (d.serial_no) { @@ -169,17 +116,6 @@ cur_frm.cscript.serial_no = function(doc, cdt, cdn) { } } - -cur_frm.cscript.warehouse = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - if (! d.item_code) {alert("please enter item code first"); return}; - if (d.warehouse) { - arg = "{'item_code':'" + d.item_code + "','warehouse':'" + d.warehouse +"'}"; - get_server_fields('get_actual_qty',arg,'delivery_note_details',doc,cdt,cdn,1); - } -} - - cur_frm.fields_dict['transporter_name'].get_query = function(doc) { return 'SELECT DISTINCT `tabSupplier`.`name` FROM `tabSupplier` WHERE `tabSupplier`.supplier_type = "transporter" AND `tabSupplier`.docstatus != 2 AND `tabSupplier`.%(key)s LIKE "%s" ORDER BY `tabSupplier`.`name` LIMIT 50'; } diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index 6ffd9600003..950d6878111 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -34,6 +34,9 @@ class DocType(SellingController): self.doclist = doclist self.tname = 'Delivery Note Item' self.fname = 'delivery_note_details' + + def set_customer_defaults(self): + self.get_default_customer_shipping_address() def validate_fiscal_year(self): get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date') @@ -78,25 +81,6 @@ class DocType(SellingController): def get_tc_details(self): return get_obj('Sales Common').get_tc_details(self) - def get_item_details(self, args=None): - import json - args = args and json.loads(args) or {} - if args.get('item_code'): - return get_obj('Sales Common').get_item_details(args, self) - else: - obj = get_obj('Sales Common') - for doc in self.doclist: - if doc.fields.get('item_code'): - arg = { - 'item_code':doc.fields.get('item_code'), - 'expense_account':doc.fields.get('expense_account'), - 'cost_center': doc.fields.get('cost_center'), - 'warehouse': doc.fields.get('warehouse')}; - ret = obj.get_item_defaults(arg) - for r in ret: - if not doc.fields.get(r): - doc.fields[r] = ret[r] - def get_barcode_details(self, barcode): return get_obj('Sales Common').get_barcode_details(barcode) @@ -105,25 +89,9 @@ class DocType(SellingController): """Re-calculates Basic Rate & amount based on Price List Selected""" get_obj('Sales Common').get_adj_percent(self) - - def get_actual_qty(self,args): - """Get Actual Qty of item in warehouse selected""" - return get_obj('Sales Common').get_available_qty(eval(args)) - - def get_rate(self,arg): return get_obj('Sales Common').get_rate(arg) - - def load_default_taxes(self): - self.doclist = get_obj('Sales Common').load_default_taxes(self) - - - def get_other_charges(self): - """Pull details from Sales Taxes and Charges Master""" - self.doclist = get_obj('Sales Common').get_other_charges(self) - - def so_required(self): """check in manage account if sales order required or not""" if webnotes.conn.get_value('Global Defaults', 'Global Defaults', 'so_required') == 'Yes': @@ -152,7 +120,6 @@ class DocType(SellingController): 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) # Set actual qty for each item in selected warehouse @@ -419,4 +386,4 @@ class DocType(SellingController): if gl_entries: from accounts.general_ledger import make_gl_entries - make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2)) \ No newline at end of file + make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2)) diff --git a/stock/doctype/delivery_note/delivery_note.txt b/stock/doctype/delivery_note/delivery_note.txt index 36c2789bfa7..4504db047ab 100644 --- a/stock/doctype/delivery_note/delivery_note.txt +++ b/stock/doctype/delivery_note/delivery_note.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-02 10:50:50", + "creation": "2013-05-24 19:29:09", "docstatus": 0, - "modified": "2013-02-02 19:18:38", + "modified": "2013-05-28 12:26:04", "modified_by": "Administrator", "owner": "Administrator" }, @@ -76,10 +76,11 @@ "search_index": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "customer_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Customer Name", "read_only": 1 @@ -96,34 +97,38 @@ "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Shipping Address", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -223,7 +228,8 @@ "oldfieldtype": "Table", "options": "Delivery Note Item", "print_hide": 0, - "read_only": 0 + "read_only": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -255,12 +261,20 @@ "oldfieldname": "net_total", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 0, + "print_hide": 1, "print_width": "150px", "read_only": 1, "reqd": 0, "width": "150px" }, + { + "doctype": "DocField", + "fieldname": "net_total_export", + "fieldtype": "Currency", + "label": "Net Total (Export)", + "options": "currency", + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "recalculate_values", @@ -435,6 +449,15 @@ "read_only": 1, "width": "150px" }, + { + "doctype": "DocField", + "fieldname": "other_charges_total_export", + "fieldtype": "Currency", + "label": "Taxes and Charges Total (Export)", + "options": "company", + "print_hide": 1, + "read_only": 1 + }, { "doctype": "DocField", "fieldname": "calculate_charges", @@ -672,6 +695,7 @@ "width": "100px" }, { + "depends_on": "customer", "doctype": "DocField", "fieldname": "contact_info", "fieldtype": "Section Break", @@ -1136,7 +1160,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material User", @@ -1159,7 +1182,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material Manager", @@ -1171,7 +1193,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "permlevel": 0, "report": 1, "role": "Sales User", @@ -1183,7 +1204,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Sales User", @@ -1205,7 +1225,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "Accounts User", "submit": 0 diff --git a/stock/doctype/delivery_note_item/delivery_note_item.txt b/stock/doctype/delivery_note_item/delivery_note_item.txt index f90ba69f532..a8eda202a05 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-04-22 13:15:44", "docstatus": 0, - "modified": "2013-05-22 12:05:32", + "modified": "2013-05-22 12:15:32", "modified_by": "Administrator", "owner": "Administrator" }, @@ -120,7 +120,7 @@ "options": "currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "reqd": 0, "width": "100px" }, @@ -189,7 +189,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "150px", - "read_only": 0, + "read_only": 1, "reqd": 0, "width": "150px" }, diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt index 9e0a2fb24e7..611ae5e937e 100644 --- a/stock/doctype/item/item.txt +++ b/stock/doctype/item/item.txt @@ -2,7 +2,7 @@ { "creation": "2013-05-03 10:45:46", "docstatus": 0, - "modified": "2013-05-22 15:48:27", + "modified": "2013-05-22 15:49:27", "modified_by": "Administrator", "owner": "Administrator" }, diff --git a/stock/doctype/material_request/material_request.js b/stock/doctype/material_request/material_request.js index 21bc141659b..6c7b2a7b885 100644 --- a/stock/doctype/material_request/material_request.js +++ b/stock/doctype/material_request/material_request.js @@ -17,8 +17,8 @@ cur_frm.cscript.tname = "Material Request Item"; cur_frm.cscript.fname = "indent_details"; -wn.require('app/buying/doctype/purchase_common/purchase_common.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/buying/doctype/purchase_common/purchase_common.js'); erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.extend({ refresh: function(doc) { @@ -48,43 +48,20 @@ erpnext.buying.MaterialRequestController = erpnext.buying.BuyingController.exten cur_frm.toggle_display("sales_order_no", false); cur_frm.fields_dict.indent_details.grid.set_column_disp("sales_order_no", false); } + }, + + validate_company_and_party: function(party_field) { + return true; + }, + + calculate_taxes_and_totals: function() { + return; } }); -var new_cscript = new erpnext.buying.MaterialRequestController({frm: cur_frm}); - // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new_cscript); - +$.extend(cur_frm.cscript, new erpnext.buying.MaterialRequestController({frm: cur_frm})); -cur_frm.cscript.onload = function(doc, cdt, cdn) { - if (!doc.transaction_date) doc.transaction_date = dateutil.obj_to_str(new Date()); - if (!doc.status) doc.status = 'Draft'; - - // defined in purchase_common.js - //cur_frm.cscript.update_item_details(doc, cdt, cdn); -}; - -cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { - // second call - if(doc.__islocal){ - cur_frm.cscript.get_item_defaults(doc); - } -}; - -cur_frm.cscript.get_item_defaults = function(doc) { - var ch = getchildren( 'Material Request Item', doc.name, 'indent_details'); - if (flt(ch.length) > 0){ - $c_obj(make_doclist(doc.doctype, doc.name), 'get_item_defaults', '', function(r, rt) {refresh_field('indent_details'); }); - } -}; - -cur_frm.cscript.transaction_date = function(doc,cdt,cdn){ - if(doc.__islocal){ - cur_frm.cscript.get_default_schedule_date(doc); - } -}; - cur_frm.cscript.qty = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; if (flt(d.qty) < flt(d.min_order_qty)) diff --git a/stock/doctype/material_request/material_request.py b/stock/doctype/material_request/material_request.py index 8a899b3a400..9f19a5b10bf 100644 --- a/stock/doctype/material_request/material_request.py +++ b/stock/doctype/material_request/material_request.py @@ -17,9 +17,6 @@ class DocType(BuyingController): self.tname = 'Material Request Item' self.fname = 'indent_details' - def get_default_schedule_date(self): - get_obj(dt = 'Purchase Common').get_default_schedule_date(self) - # get available qty at warehouse def get_bin_details(self, arg = ''): return get_obj(dt='Purchase Common').get_bin_details(arg) @@ -30,22 +27,12 @@ class DocType(BuyingController): self.check_if_already_pulled() if self.doc.sales_order_no: get_obj('DocType Mapper', 'Sales Order-Material Request', with_children=1).dt_map('Sales Order', 'Material Request', self.doc.sales_order_no, self.doc, self.doclist, "[['Sales Order', 'Material Request'],['Sales Order Item', 'Material Request Item']]") - self.get_item_defaults() else: msgprint("Please select Sales Order whose details need to pull") def check_if_already_pulled(self): pass#if self.[d.sales_order_no for d in getlist(self.doclist, 'indent_details')] - - # Get item's other details - #- ------------------------ - def get_item_defaults(self): - self.get_default_schedule_date() - for d in getlist(self.doclist, 'indent_details'): - det = webnotes.conn.sql("select min_order_qty from tabItem where name = '%s'" % d.item_code) - d.min_order_qty = det and flt(det[0][0]) or 0 - # Validate so items # ---------------------------- def validate_qty_against_so(self): diff --git a/stock/doctype/purchase_receipt/purchase_receipt.js b/stock/doctype/purchase_receipt/purchase_receipt.js index 82e494cfb75..f405a5501ec 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/stock/doctype/purchase_receipt/purchase_receipt.js @@ -19,10 +19,11 @@ cur_frm.cscript.fname = "purchase_receipt_details"; cur_frm.cscript.other_fname = "purchase_tax_details"; wn.require('app/accounts/doctype/purchase_taxes_and_charges_master/purchase_taxes_and_charges_master.js'); -wn.require('app/buying/doctype/purchase_common/purchase_common.js'); wn.require('app/utilities/doctype/sms_control/sms_control.js'); +wn.require('app/buying/doctype/purchase_common/purchase_common.js'); -erpnext.buying.PurchaseReceiptController = erpnext.buying.BuyingController.extend({ +wn.provide("erpnext.stock"); +erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({ refresh: function() { this._super(); @@ -37,43 +38,49 @@ erpnext.buying.PurchaseReceiptController = erpnext.buying.BuyingController.exten if(wn.boot.control_panel.country == 'India') { unhide_field(['challan_no', 'challan_date']); } - }, - onload_post_render: function(doc, dt, dn) { - var me = this; - var callback = function(doc, dt, dn) { - me.update_item_details(doc, dt, dn, function(r,rt) { }); + }, + + received_qty: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["qty", "received_qty"]); + + item.qty = (item.qty < item.received_qty) ? item.qty : item.received_qty; + this.qty(doc, cdt, cdn); + }, + + qty: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["qty", "received_qty"]); + + if(item.qty > item.received_qty) { + msgprint(wn._("Error") + ": " + wn._(wn.meta.get_label(item.doctype, "qty", item.name)) + + " > " + wn._(wn.meta.get_label(item.doctype, "received_qty", item.name))); + item.qty = item.rejected_qty = 0.0; + } else { + item.rejected_qty = flt(item.received_qty - item.qty, precision("rejected_qty", item)); } - // TODO: improve this - if(this.frm.doc.__islocal) { - if (this.frm.fields_dict.price_list_name && this.frm.doc.price_list_name) - this.price_list_name(callback); - else - callback(doc, dt, dn); + this._super(); + }, + + rejected_qty: function(doc, cdt, cdn) { + var item = wn.model.get_doc(cdt, cdn); + wn.model.round_floats_in(item, ["received_qty", "rejected_qty"]); + + if(item.rejected_qty > item.received_qty) { + msgprint(wn._("Error") + ": " + wn._(wn.meta.get_label(item.doctype, "rejected_qty", item.name)) + + " > " + wn._(wn.meta.get_label(item.doctype, "received_qty", item.name))); + item.qty = item.rejected_qty = 0.0; + } else { + item.qty = flt(item.received_qty - item.rejected_qty, precision("qty", item)); } - } + + this.qty(doc, cdt, cdn); + }, }); -var new_cscript = new erpnext.buying.PurchaseReceiptController({frm: cur_frm}); - // for backward compatibility: combine new and previous states -$.extend(cur_frm.cscript, new_cscript); - -cur_frm.cscript.onload = function(doc, cdt, cdn) { - if(!doc.fiscal_year && doc.__islocal){ wn.model.set_default_values(doc);} - if (!doc.posting_date) doc.posting_date = dateutil.obj_to_str(new Date()); - if (!doc.transaction_date) doc.transaction_date = dateutil.obj_to_str(new Date()); - if (!doc.status) doc.status = 'Draft'; -} - -cur_frm.cscript.supplier = function(doc,dt,dn) { - if (doc.supplier) { - get_server_fields('get_default_supplier_address', - JSON.stringify({ supplier: doc.supplier }),'', doc, dt, dn, 1, function() { - cur_frm.refresh(); - }); - } -} +$.extend(cur_frm.cscript, new erpnext.stock.PurchaseReceiptController({frm: cur_frm})); cur_frm.cscript.supplier_address = cur_frm.cscript.contact_person = function(doc,dt,dn) { if(doc.supplier) get_server_fields('get_supplier_address', JSON.stringify({supplier: doc.supplier, address: doc.supplier_address, contact: doc.contact_person}),'', doc, dt, dn, 1); @@ -151,81 +158,6 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) return 'SELECT `tabPrint Heading`.name FROM `tabPrint Heading` WHERE `tabPrint Heading`.docstatus !=2 AND `tabPrint Heading`.name LIKE "%s" ORDER BY `tabPrint Heading`.name ASC LIMIT 50'; } -//========================= Received Qty ============================================================= - -cur_frm.cscript.received_qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - ret = { - 'qty' : (flt(d.qty) && flt(d.qty) < flt(d.received_qty)) - ? flt(d.qty) : flt(d.received_qty), - 'stock_qty': 0, - 'rejected_qty' : 0, - } - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - cur_frm.cscript.calc_amount(doc, 2); -} - -//======================== Qty (Accepted Qty) ========================================================= - -cur_frm.cscript.qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - // Step 1 :=> Check If Qty > Received Qty - if (flt(d.qty) > flt(d.received_qty)) { - alert("Accepted Qty cannot be greater than Received Qty") - ret = { - 'qty' : 0, - 'stock_qty': 0, - 'rejected_qty' : 0 - } - // => Set Qty = 0 and rejected_qty = 0 - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - cur_frm.cscript.calc_amount(doc, 2); - // => Return - return - } - // Step 2 :=> Check IF Qty <= REceived Qty - else { - ret = { - 'rejected_qty':flt(d.received_qty) - flt(d.qty) - } - // => Set Rejected Qty = Received Qty - Qty - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - // => Calculate Amount - cur_frm.cscript.calc_amount(doc, 2); - cur_frm.cscript.update_stock_qty(doc,cdt,cdn); - } -} - -//======================== Rejected Qty ========================================================= -cur_frm.cscript.rejected_qty = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - // Step 1 :=> Check If Rejected Qty > Received Qty - if (flt(d.rejected_qty) > flt(d.received_qty)) { - alert("Rejected Qty cannot be greater than Received Qty") - ret = { - 'qty' : 0, - 'stock_qty': 0, - 'rejected_qty' : 0 - } - // => Set Qty = 0 and rejected_qty = 0 - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - cur_frm.cscript.calc_amount(doc, 2); - // => Return - return - } - // Step 2 :=> Check IF Rejected Qty <= REceived Qty - else { - ret = { - 'qty':flt(d.received_qty) - flt(d.rejected_qty) - } - // => Set Qty = Received Qty - Rejected Qty - set_multiple('Purchase Receipt Item', cdn, ret, 'purchase_receipt_details'); - // Calculate Amount - cur_frm.cscript.calc_amount(doc, 2); - cur_frm.cscript.update_stock_qty(doc,cdt,cdn); - } -} - //================================= Purchase Order No Get Query ==================================== cur_frm.fields_dict['purchase_order_no'].get_query = function(doc) { if (doc.supplier) diff --git a/stock/doctype/purchase_receipt/purchase_receipt.txt b/stock/doctype/purchase_receipt/purchase_receipt.txt index 350777d95c1..4e055dcd541 100755 --- a/stock/doctype/purchase_receipt/purchase_receipt.txt +++ b/stock/doctype/purchase_receipt/purchase_receipt.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-25 16:01:29", + "creation": "2013-05-21 16:16:39", "docstatus": 0, - "modified": "2013-02-02 19:09:37", + "modified": "2013-05-28 12:21:17", "modified_by": "Administrator", "owner": "Administrator" }, @@ -75,43 +75,48 @@ "width": "150px" }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "supplier_name", "fieldtype": "Data", - "hidden": 1, + "hidden": 0, "in_list_view": 1, "label": "Supplier Name", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "address_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Address", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_display", "fieldtype": "Small Text", - "hidden": 1, + "hidden": 0, "label": "Contact", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_mobile", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Mobile No", "read_only": 1 }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_email", "fieldtype": "Text", - "hidden": 1, + "hidden": 0, "label": "Contact Email", "print_hide": 1, "read_only": 1 @@ -569,6 +574,7 @@ "oldfieldtype": "Text Editor" }, { + "depends_on": "supplier", "doctype": "DocField", "fieldname": "contact_section", "fieldtype": "Section Break", @@ -888,7 +894,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material Manager", @@ -911,7 +916,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "report": 0, "role": "Material User", @@ -934,7 +938,6 @@ "cancel": 1, "create": 1, "doctype": "DocPerm", - "match": "", "permlevel": 0, "report": 1, "role": "Purchase User", @@ -946,7 +949,6 @@ "cancel": 0, "create": 0, "doctype": "DocPerm", - "match": "", "permlevel": 1, "role": "Purchase User", "submit": 0 diff --git a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt index 8cef6a35340..9f7d72c0718 100755 --- a/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt +++ b/stock/doctype/purchase_receipt_item/purchase_receipt_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-03-07 11:42:59", + "creation": "2013-05-24 19:29:10", "docstatus": 0, - "modified": "2013-05-22 12:01:08", + "modified": "2013-05-28 12:13:59", "modified_by": "Administrator", "owner": "Administrator" }, @@ -357,7 +357,7 @@ "oldfieldtype": "Currency", "print_hide": 1, "print_width": "100px", - "read_only": 0, + "read_only": 1, "width": "100px" }, { @@ -370,7 +370,7 @@ "oldfieldname": "prevdoc_doctype", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 0 + "read_only": 1 }, { "doctype": "DocField", diff --git a/stock/doctype/serial_no/test_serial_no.py b/stock/doctype/serial_no/test_serial_no.py index fb27aa9329a..8ffaabcb10c 100644 --- a/stock/doctype/serial_no/test_serial_no.py +++ b/stock/doctype/serial_no/test_serial_no.py @@ -81,8 +81,8 @@ class TestSerialNo(unittest.TestCase): self.assertFalse(gl_entries) webnotes.defaults.set_global_default("auto_inventory_accounting", 0) - - + +test_dependencies = ["Item"] test_records = [ [ { diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py index c3ce2d7f40b..a9281cd004f 100644 --- a/stock/doctype/stock_entry/test_stock_entry.py +++ b/stock/doctype/stock_entry/test_stock_entry.py @@ -450,6 +450,7 @@ class TestStockEntry(unittest.TestCase): for d in pi.doclist.get({"parentfield": "entries"}): d.expense_head = "_Test Account Cost for Goods Sold - _TC" d.cost_center = "_Test Cost Center - _TC" + for d in pi.doclist.get({"parentfield": "purchase_tax_details"}): d.cost_center = "_Test Cost Center - _TC" diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 5d7d1a84b1a..5b716fcc530 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -16,6 +16,7 @@ from __future__ import unicode_literals import webnotes +from webnotes import msgprint, _ from webnotes.utils import load_json, cstr, flt, now_datetime from webnotes.model.doc import addchild @@ -191,7 +192,8 @@ class TransactionBase(DocListController): # Get Supplier Default Primary Address - first load # ----------------------- def get_default_supplier_address(self, args): - args = load_json(args) + if isinstance(args, basestring): + args = load_json(args) address_text, address_name = self.get_address_text(supplier=args['supplier']) ret = { 'supplier_address' : address_name, @@ -268,4 +270,56 @@ class TransactionBase(DocListController): def validate_posting_time(self): if not self.doc.posting_time: self.doc.posting_time = now_datetime().strftime('%H:%M:%S') + +def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company): + """common validation for currency and price list currency""" + if conversion_rate == 0: + msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True) + + company_currency = webnotes.conn.get_value("Company", company, "default_currency") + + # parenthesis for 'OR' are necessary as we want it to evaluate as + # mandatory valid condition and (1st optional valid condition + # or 2nd optional valid condition) + valid_conversion_rate = (conversion_rate and + ((currency == company_currency and conversion_rate == 1.00) + or (currency != company_currency and conversion_rate != 1.00))) + + if not valid_conversion_rate: + msgprint(_('Please enter valid ') + conversion_rate_label + (': ') + + ("1 %s = [?] %s" % (currency, company_currency)), + raise_exception=True) + +def validate_item_fetch(args, item): + from stock.utils import validate_end_of_life + validate_end_of_life(item.name, item.end_of_life) + + # validate company + if not args.company: + msgprint(_("Please specify Company"), raise_exception=True) + +def validate_currency(args, item, meta=None): + from webnotes.model.meta import get_field_precision + if not meta: + meta = webnotes.get_doctype(args.doctype) + + # validate conversion rate + if meta.get_field("currency"): + validate_conversion_rate(args.currency, args.conversion_rate, + meta.get_label("conversion_rate"), args.company) + + # round it + args.conversion_rate = flt(args.conversion_rate, + get_field_precision(meta.get_field("conversion_rate"), args)) + + # validate price list conversion rate + if meta.get_field("price_list_currency") and args.price_list_name and \ + args.price_list_currency: + validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate, + meta.get_label("plc_conversion_rate"), args.company) + + # round it + args.plc_conversion_rate = flt(args.plc_conversion_rate, + get_field_precision(meta.get_field("plc_conversion_rate"), args)) + \ No newline at end of file