From f309613a7d4d5befc880e5d499d7564dd7c8e1c1 Mon Sep 17 00:00:00 2001 From: Anand Doshi Date: Tue, 21 May 2013 19:35:06 +0530 Subject: [PATCH] [selling] [calculations] client side calculations, cleanup, patch fixes --- .../purchase_invoice/purchase_invoice.js | 4 +- .../doctype/sales_invoice/sales_invoice.js | 189 +-- .../doctype/sales_invoice/sales_invoice.py | 128 +- .../doctype/sales_invoice/sales_invoice.txt | 18 +- .../sales_invoice_item/sales_invoice_item.txt | 8 +- .../purchase_common/purchase_common.js | 27 +- buying/utils.py | 69 +- controllers/selling_controller.py | 197 ++- .../may_2013/p01_selling_net_total_export.py | 23 +- selling/doctype/quotation/quotation.py | 11 - selling/doctype/quotation/quotation.txt | 16 +- .../doctype/quotation_item/quotation_item.txt | 6 +- selling/doctype/sales_common/sales_common.js | 1321 ++++++++--------- selling/doctype/sales_common/sales_common.py | 313 ++-- selling/doctype/sales_order/sales_order.py | 7 - selling/doctype/sales_order/sales_order.txt | 18 +- .../sales_order_item/sales_order_item.txt | 6 +- selling/doctype/sales_team/sales_team.txt | 10 +- selling/utils.py | 69 +- stock/doctype/delivery_note/delivery_note.py | 11 - stock/doctype/delivery_note/delivery_note.txt | 16 +- .../delivery_note_item/delivery_note_item.txt | 8 +- utilities/transaction_base.py | 17 +- 23 files changed, 1174 insertions(+), 1318 deletions(-) diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.js b/accounts/doctype/purchase_invoice/purchase_invoice.js index 92f17487bc..b498ce25af 100644 --- a/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -54,10 +54,8 @@ erpnext.buying.PurchaseInvoiceController = erpnext.buying.BuyingController.exten } }); -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.buying.PurchaseInvoiceController({frm: cur_frm})); cur_frm.cscript.onload = function(doc,dt,dn) { diff --git a/accounts/doctype/sales_invoice/sales_invoice.js b/accounts/doctype/sales_invoice/sales_invoice.js index be6ec3d001..800cd2bba5 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.js +++ b/accounts/doctype/sales_invoice/sales_invoice.js @@ -26,48 +26,62 @@ 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'); -// 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}); +erpnext.selling.SalesInvoiceController = erpnext.selling.SellingController.extend({ + onload: function() { + this._super(); + + // 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); + } + }, + + refresh: function(doc, dt, dn) { + this._super(); + + cur_frm.cscript.is_opening(doc, dt, dn); - } -} + // Show / Hide button + cur_frm.clear_custom_buttons(); + // if (!cur_frm.cscript.is_onload) cur_frm.cscript.hide_price_list_currency(doc, dt, dn); -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); - } - ); + 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", + }); } } - // 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); - } - 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(); + }, +}); +// for backward compatibility: combine new and previous states +$.extend(cur_frm.cscript, new erpnext.selling.SalesInvoiceController({frm: cur_frm})); // Hide Fields // ------------ @@ -104,50 +118,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", @@ -168,66 +138,10 @@ cur_frm.cscript.warehouse = function(doc, cdt , cdn) { } } - - -//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); @@ -244,9 +158,6 @@ 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); diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index 615da19a70..e58b4000f2 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() @@ -134,25 +132,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"}): @@ -173,10 +162,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: @@ -193,7 +183,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 @@ -203,7 +193,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""" @@ -272,86 +262,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) @@ -375,14 +293,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") @@ -611,7 +521,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)) diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt index f1c0cab74d..8b114e7ffb 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.txt +++ b/accounts/doctype/sales_invoice/sales_invoice.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-06 12:03:41", + "creation": "2013-05-21 16:16:41", "docstatus": 0, - "modified": "2013-05-09 17:34:14", + "modified": "2013-05-21 18:25:07", "modified_by": "Administrator", "owner": "Administrator" }, @@ -224,11 +224,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", @@ -460,6 +461,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", diff --git a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt index 2a6384d762..4dcd901f28 100644 --- a/accounts/doctype/sales_invoice_item/sales_invoice_item.txt +++ b/accounts/doctype/sales_invoice_item/sales_invoice_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-10 08:35:44", + "creation": "2013-04-19 13:30:26", "docstatus": 0, - "modified": "2013-04-17 14:05:20", + "modified": "2013-05-21 16:43:21", "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/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js index ff875bae64..fa2138afdc 100644 --- a/buying/doctype/purchase_common/purchase_common.js +++ b/buying/doctype/purchase_common/purchase_common.js @@ -84,20 +84,8 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ item_code: function(doc, cdt, cdn) { var me = this; var item = wn.model.get_doc(cdt, cdn); - - // validate company if(item.item_code) { - var fetch = true; - $.each(["company", "supplier"], function(i, fieldname) { - if(!me.frm.doc[fieldname]) { - fetch = false; - msgprint(wn._("Please specify") + ": " + - wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + - ". " + wn._("It is needed to fetch Item Details.")); - } - }); - - if(!fetch) { + if(!this.validate_company_and_party()) { item.item_code = null; refresh_field("item_code", item.name, item.parentfield); } else { @@ -128,6 +116,19 @@ erpnext.buying.BuyingController = wn.ui.form.Controller.extend({ } }, + validate_company_and_party: function() { + var valid = true; + $.each(["company", "supplier"], function(i, fieldname) { + if(!me.frm.doc[fieldname]) { + valid = false; + msgprint(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 valid; + }, + update_item_details: function(doc, dt, dn, callback) { if(!this.frm.doc.__islocal) return; diff --git a/buying/utils.py b/buying/utils.py index 54197b49fb..952d70a602 100644 --- a/buying/utils.py +++ b/buying/utils.py @@ -59,23 +59,12 @@ def get_item_details(args): 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 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_bean.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) + 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)) return out @@ -100,34 +89,38 @@ def _get_basic_details(args, item_bean): return out +def _get_price_list_rate(args, item_bean, meta=None): + 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.purchase_ref_rate = flt(price_list_rate[0].ref_rate) * flt(args.plc_conversion_rate) + + # if not found, fetch from last purchase transaction + if not out.purchase_ref_rate: + last_purchase = get_last_purchase_details(item.name, args.docname, args.conversion_rate) + if last_purchase: + out.update(last_purchase) + + if out.purchase_ref_rate or out.purchase_rate or out.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 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}) - - 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() - def _validate_item_details(args, item): from utilities.transaction_base import validate_item_fetch validate_item_fetch(args, item) diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 63b87e1eea..a92ff88c48 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -16,7 +16,7 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import cint, flt +from webnotes.utils import cint, flt, comma_or from setup.utils import get_company_currency from webnotes import msgprint, _ import json @@ -24,9 +24,73 @@ import json from controllers.stock_controller import StockController class SellingController(StockController): + def onload_post_render(self): + self.set_price_list_currency() + + # contact, address, item details and pos details (if applicable) + self.set_missing_values() + + if self.meta.get_field("other_charges"): + self.set_taxes() + def validate(self): super(SellingController, self).validate() + # self.calculate_taxes_and_totals() self.set_total_in_words() + self.set_missing_values(for_validate=True) + + def set_price_list_currency(self): + if self.doc.price_list_name and not self.doc.price_list_currency: + # TODO - change this, since price list now has only one currency allowed + from setup.utils import get_price_list_currency + self.doc.fields.update(get_price_list_currency( + {"price_list_name": self.doc.price_list_name, "use_for": "selling"})) + + 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 + + # set missing item values + from selling.utils import get_item_details + for item in self.doclist.get({"parentfield": "entries"}): + if item.fields.get("item_code"): + ret = get_item_details(item.fields) + 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_taxes(self): + if not self.doclist.get({"parentfield": "other_charges"}): + if not self.doc.charge: + # get the default tax master + self.doc.charge = webnotes.conn.get_value("Sales Taxes and Charges Master", + {"is_default": 1}) + + if self.doc.charge: + from webnotes.model import default_fields + tax_master = webnotes.bean("Sales Taxes and Charges Master", self.doc.charge) + for i, tax in enumerate(tax_master.doclist.get({"parentfield": "other_charges"})): + for fieldname in default_fields: + tax.fields[fieldname] = None + + tax.fields.update({ + "doctype": "Sales Taxes and Charges", + "parentfield": "other_charges", + "idx": i+1 + }) + + self.doclist.append(tax) + + def get_other_charges(self): + self.doclist = self.doc.clear_table(self.doclist, "other_charges") + self.set_taxes() + + def set_customer_defaults(self): + self.get_default_customer_address() def set_total_in_words(self): from webnotes.utils import money_in_words @@ -81,25 +145,19 @@ class SellingController(StockController): self.calculate_item_values() self.initialize_taxes() - - self.determin_exclusive_rate() - - # TODO - # code: save net_total_export on client side - # print format: show net_total_export instead of net_total - + self.determine_exclusive_rate() self.calculate_net_total() self.calculate_taxes() self.calculate_totals() + self.calculate_commission() + self.calculate_contribution() # self.calculate_outstanding_amount() - - # TODO - # allocated amount of sales person - # total commission - self._cleanup() - def determin_exclusive_rate(self): + # TODO + # print format: show net_total_export instead of net_total + + def determine_exclusive_rate(self): if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)): # no inclusive tax return @@ -108,15 +166,10 @@ class SellingController(StockController): item_tax_map = self._load_item_tax_rate(item.item_tax_rate) cumulated_tax_fraction = 0 for i, tax in enumerate(self.tax_doclist): - if cint(tax.included_in_print_rate): - tax.tax_fraction_for_current_item = \ - self.get_current_tax_fraction(tax, item_tax_map) - else: - tax.tax_fraction_for_current_item = 0 - + 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 + 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 \ @@ -130,8 +183,12 @@ class SellingController(StockController): item.amount = flt(item.basic_rate * item.qty, self.precision("amount", item)) - item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)), - self.precision("base_ref_rate", 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): """ @@ -188,24 +245,23 @@ class SellingController(StockController): def initialize_taxes(self): for tax in self.tax_doclist: tax.tax_amount = tax.total = 0.0 + tax.item_wise_tax_detail = {} + # 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) self.validate_inclusive_tax(tax) self.round_floats_in(tax) def calculate_net_total(self): - self.doc.net_total = 0 - self.doc.net_total_export = 0 + 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.doc.net_total = flt(self.doc.net_total, self.precision("net_total")) - self.doc.net_total_export = flt(self.doc.net_total_export, - self.precision("net_total_export")) + + self.round_floats_in(self.doc, ["net_total", "net_total_export"]) def calculate_taxes(self): for item in self.item_doclist: @@ -218,8 +274,8 @@ class SellingController(StockController): # 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": + 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 @@ -228,7 +284,7 @@ class SellingController(StockController): 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 + tax.tax_amount += current_tax_amount # Calculate tax.total viz. grand total till that step # note: grand_total_for_current_item contains the contribution of @@ -245,8 +301,7 @@ class SellingController(StockController): # 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? + # store tax breakup for each item tax.item_wise_tax_detail[item.item_code] = current_tax_amount def calculate_totals(self): @@ -254,12 +309,43 @@ class SellingController(StockController): 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_commission(self): + if self.doc.commission_rate > 100: + 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 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 @@ -288,20 +374,18 @@ class SellingController(StockController): msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \ _("Please specify a valid") + " %(row_id_label)s") % { "idx": tax.idx, - "taxes_doctype": tax.parenttype, + "taxes_doctype": tax.doctype, "row_id_label": self.meta.get_label("row_id", parentfield="other_charges") }, raise_exception=True) def validate_inclusive_tax(self, tax): - def _on_previous_row_error(tax, row_range): - msgprint((_("Row") - + " # %(idx)s [%(taxes_doctype)s] [%(charge_type_label)s = \"%(charge_type)s\"]: " - + _("If:") + ' "%(inclusive_label)s" = ' + _("checked") + ", " - + _("then it is required that:") + " [" + _("Row") + " # %(row_range)s] " - + '"%(inclusive_label)s" = ' + _("checked")) % { + 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, - "taxes_doctype": tax.doctype, + "doctype": tax.doctype, "inclusive_label": self.meta.get_label("included_in_print_rate", parentfield="other_charges"), "charge_type_label": self.meta.get_label("charge_type", @@ -312,12 +396,12 @@ class SellingController(StockController): if cint(tax.included_in_print_rate): if tax.charge_type == "Actual": - # inclusive cannot be of type Actual + # inclusive tax cannot be of type Actual msgprint((_("Row") - + " # %(idx)s [%(taxes_doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" " + + " # %(idx)s [%(doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" " + "cannot be included in Item's rate") % { "idx": tax.idx, - "taxes_doctype": tax.doctype, + "doctype": tax.doctype, "charge_type_label": self.meta.get_label("charge_type", parentfield="other_charges"), "charge_type": tax.charge_type, @@ -325,16 +409,14 @@ class SellingController(StockController): 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, tax.row_id) + _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.idx - 1]]): - # all rows about this tax should be inclusive - _on_previous_row_error(tax, "1 - %d" % (tax.idx - 1,)) + 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 _load_item_tax_rate(self, item_tax_rate): - if not item_tax_rate: - return {} - return json.loads(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): @@ -344,10 +426,9 @@ class SellingController(StockController): 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"] - - for fieldname in ("tax_fraction_for_current_item", + 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] diff --git a/patches/may_2013/p01_selling_net_total_export.py b/patches/may_2013/p01_selling_net_total_export.py index dd0f68ac0a..7d4e3463b9 100644 --- a/patches/may_2013/p01_selling_net_total_export.py +++ b/patches/may_2013/p01_selling_net_total_export.py @@ -1,10 +1,29 @@ 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)""" % - (doctype,)) \ No newline at end of file + 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")): + if cint(webnotes.conn.get_value("DocField", {"parent": doctype, "fieldname": "ref_rate"}, "read_only")) == 0 and \ + not webnotes.conn.sql("""select name from `tabProperty Setter` where doc_type=%s and doctype_or_field='DocField' + and field_name='ref_rate' and property='read_only'""", doctype): + webnotes.bean({ + "doctype": "Property Setter", + "doc_type": doctype, + "doctype_or_field": "DocField", + "field_name": "ref_rate", + "property": "read_only", + "property_type": "Check", + "value": "0" + }).insert() + + webnotes.reload_doc(module, "DocType", doctype) \ No newline at end of file diff --git a/selling/doctype/quotation/quotation.py b/selling/doctype/quotation/quotation.py index 7e83131204..f4c30fcb93 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): diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt index 24a080bac1..01b34ae1e0 100644 --- a/selling/doctype/quotation/quotation.txt +++ b/selling/doctype/quotation/quotation.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-06 12:03:40", + "creation": "2013-05-21 16:16:40", "docstatus": 0, - "modified": "2013-05-06 13:07:37", + "modified": "2013-05-21 18:29:59", "modified_by": "Administrator", "owner": "Administrator" }, @@ -214,6 +214,7 @@ "oldfieldtype": "Table", "options": "Quotation Item", "read_only": 0, + "reqd": 1, "width": "40px" }, { @@ -431,13 +432,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", diff --git a/selling/doctype/quotation_item/quotation_item.txt b/selling/doctype/quotation_item/quotation_item.txt index dccc503764..38d2fcf888 100644 --- a/selling/doctype/quotation_item/quotation_item.txt +++ b/selling/doctype/quotation_item/quotation_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:52", + "creation": "2013-04-19 13:30:50", "docstatus": 0, - "modified": "2013-03-07 07:03:29", + "modified": "2013-05-21 16:45:44", "modified_by": "Administrator", "owner": "Administrator" }, @@ -115,6 +115,7 @@ "options": "currency", "print_hide": 1, "print_width": "100px", + "read_only": 1, "reqd": 0, "width": "100px" }, @@ -188,6 +189,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "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 67c7539b05..85e9fd7aab 100644 --- a/selling/doctype/sales_common/sales_common.js +++ b/selling/doctype/sales_common/sales_common.js @@ -25,28 +25,78 @@ wn.provide("erpnext.selling"); erpnext.selling.SellingController = wn.ui.form.Controller.extend({ setup: function() { - + this.frm.add_fetch("sales_partner", "commission_rate", "commission_rate"); }, + // events when rendering form + // 1 + onload: function() { + var me = this; + this.toggle_rounded_total(); + if(this.frm.doc.__islocal) { + // set date fields + $.each(["posting_date", "due_date", "transaction_date"], function(i, fieldname) { + if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) { + me.frm.set_value(fieldname, get_today()); + } + }); + + // set currency fields + $.each(["currency", "price_list_currency"], function(i, fieldname) { + if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) { + me.frm.set_value(fieldname, wn.defaults.get_default("currency")); + } + }); + + // status + if(!this.frm.doc.status) this.frm.set_value("status", "Draft"); + + // TODO set depends_on for customer related fields + } + }, + + // 2 refresh: function() { + erpnext.hide_naming_series(); + this.toggle_price_list_fields(); + // TODO + // display item wise taxes in an html table + }, + + // 3 + 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(); + + // TODO calc adjustment amount + }, + + 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) { - var fetch = true; - $.each(["company", "customer"], function(i, fieldname) { - if(!me.frm.doc[fieldname]) { - fetch = false; - msgprint(wn._("Please specify") + ": " + - wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + - ". " + wn._("It is needed to fetch Item Details.")); - } - }); - - if(!fetch) { + if(item.item_code || item.barcode) { + if(!this.validate_company_and_party()) { item.item_code = null; refresh_field("item_code", item.name, item.parentfield); } else { @@ -56,6 +106,7 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ args: { args: { item_code: item.item_code, + barcode: item.barcode, warehouse: item.warehouse, doctype: me.frm.doc.doctype, customer: me.frm.doc.customer, @@ -65,19 +116,186 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ 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 - + 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) { - // TODO: calculate + if(!r.exc) { + me.ref_rate(me.frm.doc, cdt, cdn); + } } }); } } }, - update_item_details: function() { + company: function() { + if(this.frm.doc.company) { + var me = this; + var company_currency = wn.model.get_doc(":Company", this.frm.doc.company).default_currency; + $.each(["currency", "price_list_currency"], function(i, fieldname) { + if(!me.doc[fieldname]) { + me.frm.set_value(fieldname, company_currency); + + // TODO - check this + me.frm.runclientscript(fieldname); + } + }); + } + }, + + customer: function() { + 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 me = this; + 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(); + if(me.frm.doc.price_list_name !== price_list_name) me.price_list_name(); + } + } + }); + } + } + + // TODO hide/unhide related fields + }, + + // TODO + price_list_name: function() { + console.log("price_list_name"); + }, + + 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(); + }, + + qty: function(doc, cdt, cdn) { + this.calculate_taxes_and_totals(); + }, + + adj_rate: function(doc, cdt, cdn) { + 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(); + }, + + 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); + } catch(e) { + tax.included_in_print_rate = 0; + refresh_field("included_in_print_rate", tax.name, tax.parentfield); + throw e; + } + }, + + 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); + } + }, + + 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); + }); + } + }, + + validate_company_and_party: function() { + var me = this; + var valid = true; + $.each(["company", "customer"], function(i, fieldname) { + if(!me.frm.doc[fieldname]) { + valid = false; + msgprint(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 valid; + }, + + 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); + } + }); + }, + + // TODO + toggle_price_list_fields: function() { }, @@ -85,7 +303,359 @@ erpnext.selling.SellingController = wn.ui.form.Controller.extend({ }, + calculate_taxes_and_totals: function() { + this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); + + // TODO 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.calculate_net_total(); + this.calculate_taxes(); + this.calculate_totals(); + this.calculate_commission(); + this.calculate_contribution(); + this._cleanup(); + + this.frm.doc.in_words = this.frm.doc.in_words_export = ""; + + // TODO + // outstanding amount + + // check for custom_recalc in custom scripts of server + + this.frm.refresh(); + + }, + 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: "other_charges"}); + }, + + calculate_item_values: function() { + var me = this; + + var _set_base = function(item, print_field, base_field) { + // set values in base currency + item[base_field] = flt(item[print_field] * me.frm.doc.conversion_rate, + precision(base_field, item)); + }; + + $.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)); + + _set_base(item, "ref_rate", "base_ref_rate"); + _set_base(item, "export_rate", "basic_rate"); + _set_base(item, "export_amount", "amount"); + }); + + }, + + initialize_taxes: function() { + var me = this; + $.each(this.frm.tax_doclist, function(i, tax) { + tax.tax_amount = tax.total = 0.0; + tax.item_wise_tax_detail = {}; + + // temporary fields + tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0; + + me.validate_on_previous_row(tax); + me.validate_inclusive_tax(tax); + + wn.model.round_floats_in(tax); + }); + }, + + determine_exclusive_rate: function() { + var me = this; + $.each(me.frm.item_doclist, function(n, item) { + var item_tax_map = me._load_item_tax_rate(item.item_tax_rate); + var cumulated_tax_fraction = 0.0; + + $.each(me.frm.tax_doclist, function(i, tax) { + tax.tax_fraction_for_current_item = me.get_current_tax_fraction(tax, item_tax_map); + + if(i==0) { + tax.grand_total_for_current_item = 1 + tax.tax_fraction_for_current_item; + } else { + tax.grand_total_for_current_item = + me.frm.tax_doclist[i-1].grand_total_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 * me.frm.doc.conversion_rate) / (1 + cumulated_tax_fraction), + precision("basic_rate", item)); + + item.amount = flt(item.basic_rate * item.qty, 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), + precision("base_ref_rate", item)); + } + } + }); + }, + + 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; + + if(cint(tax.included_in_print_rate)) { + var tax_rate = me._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) * + me.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) * + me.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item; + } + } + + return current_tax_fraction; + }, + + calculate_net_total: function() { + var me = this; + + 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_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); + + // 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; + + // 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; + + // store tax breakup for each item + tax.item_wise_tax_detail[item.item_code || item.item_name] = current_tax_amount; + + }); + }); + }, + + 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); + + } else if(tax.charge_type == "On Previous Row Amount") { + current_tax_amount = (tax_rate / 100.0) * + me.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) * + me.frm.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item; + + } + + return flt(current_tax_amount, precision("tax_amount", tax)); + }, + + 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_commission: function() { + 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() { + $.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() { + $.each(this.frm.tax_doclist, function(i, tax) { + var tax_fields = keys(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); + }); + }, + + 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.get(tax.account_head), precision("rate", tax)) : + tax.rate; + }, }); // to save previous state of cur_frm.cscript @@ -98,65 +668,6 @@ cur_frm.cscript = new erpnext.selling.SellingController({frm: cur_frm}); $.extend(cur_frm.cscript, prev_cscript); -// ============== 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); - } - } 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); - }); - } -} - - -// 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 \ - 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 \ @@ -491,482 +906,4 @@ cur_frm.cscript.get_charges = function(doc, cdt, cdn, callback) { } -// 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); - } - - // TODO: store net_total_export - - 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_details(self, args, obj): + # import json + # if not obj.doc.price_list_name: + # msgprint("Please Select Price List before selecting Items", raise_exception=True) + # item = webnotes.conn.sql("""select description, item_name, brand, item_group, stock_uom, + # default_warehouse, default_income_account, default_sales_cost_center, + # purchase_account, description_html, barcode 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) + # 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 + # TODO: deprecate it 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 @@ -200,97 +183,78 @@ class DocType(TransactionBase): 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 + # 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_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) + # 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 + # # 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 +291,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 +299,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.py b/selling/doctype/sales_order/sales_order.py index c74e7e1f9d..535a44ef65 100644 --- a/selling/doctype/sales_order/sales_order.py +++ b/selling/doctype/sales_order/sales_order.py @@ -83,12 +83,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) @@ -225,7 +219,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 9780dc71a3..fdf60a21ec 100644 --- a/selling/doctype/sales_order/sales_order.txt +++ b/selling/doctype/sales_order/sales_order.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-06 12:03:43", + "creation": "2013-05-21 16:16:41", "docstatus": 0, - "modified": "2013-05-06 13:06:37", + "modified": "2013-05-21 18:30:14", "modified_by": "Administrator", "owner": "Administrator" }, @@ -230,7 +230,8 @@ "oldfieldname": "sales_order_details", "oldfieldtype": "Table", "options": "Sales Order Item", - "print_hide": 0 + "print_hide": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -434,7 +435,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", @@ -442,6 +443,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": "other_charges_calculation", diff --git a/selling/doctype/sales_order_item/sales_order_item.txt b/selling/doctype/sales_order_item/sales_order_item.txt index fff2d080f3..6455807107 100644 --- a/selling/doctype/sales_order_item/sales_order_item.txt +++ b/selling/doctype/sales_order_item/sales_order_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-02-22 01:27:52", + "creation": "2013-04-19 13:30:51", "docstatus": 0, - "modified": "2013-03-07 07:03:30", + "modified": "2013-05-21 16:44:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -109,6 +109,7 @@ "options": "currency", "print_hide": 1, "print_width": "70px", + "read_only": 1, "reqd": 0, "width": "70px" }, @@ -176,6 +177,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "print_width": "100px", + "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 add466c334..29a951eef7 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 23574dfa9e..5e986eeda7 100644 --- a/selling/utils.py +++ b/selling/utils.py @@ -17,7 +17,7 @@ from __future__ import unicode_literals import webnotes from webnotes import msgprint, _ -from webnotes.utils import flt +from webnotes.utils import flt, cint, comma_and import json def get_customer_list(doctype, txt, searchfield, start, page_len, filters): @@ -51,22 +51,45 @@ def get_item_details(args): 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) - if args.price_list_name and args.price_list_currency: - out.update(_get_price_list_rate(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, 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) @@ -106,18 +129,21 @@ def _get_basic_details(args, item_bean): return out -def _get_price_list_rate(args, item_bean): +def _get_price_list_rate(args, item_bean, meta=None): 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}) - out = webnotes._dict() - out.base_ref_rate = flt(base_ref_rate[0].ref_rate) if base_ref_rate else 0.0 - out.basic_rate = out.base_ref_rate - out.ref_rate = out.base_ref_rate / flt(args.conversion_rate) - out.export_rate = out.ref_rate - return out + + 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 {"base_ref_rate": flt(base_ref_rate[0].ref_rate / args.plc_conversion_rate)} def _get_available_qty(args, warehouse): return webnotes.conn.get_value("Bin", {"item_code": args.item_code, "warehouse": warehouse}, @@ -128,4 +154,25 @@ def _get_customer_item_code(args, item_bean): "customer_name": args.customer}) return customer_item_code and customer_item_code[0].ref_code or None - \ No newline at end of file + +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, out.get("warehouse")).get("actual_qty") + + return out diff --git a/stock/doctype/delivery_note/delivery_note.py b/stock/doctype/delivery_note/delivery_note.py index 15e24ef293..93ca721f92 100644 --- a/stock/doctype/delivery_note/delivery_note.py +++ b/stock/doctype/delivery_note/delivery_note.py @@ -114,16 +114,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): - """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 +142,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 diff --git a/stock/doctype/delivery_note/delivery_note.txt b/stock/doctype/delivery_note/delivery_note.txt index 6f299efb15..72c6f47188 100644 --- a/stock/doctype/delivery_note/delivery_note.txt +++ b/stock/doctype/delivery_note/delivery_note.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-05-06 12:03:30", + "creation": "2013-05-21 16:16:31", "docstatus": 0, - "modified": "2013-05-06 13:08:13", + "modified": "2013-05-21 18:30:32", "modified_by": "Administrator", "owner": "Administrator" }, @@ -223,7 +223,8 @@ "oldfieldtype": "Table", "options": "Delivery Note Item", "print_hide": 0, - "read_only": 0 + "read_only": 0, + "reqd": 1 }, { "doctype": "DocField", @@ -443,6 +444,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", diff --git a/stock/doctype/delivery_note_item/delivery_note_item.txt b/stock/doctype/delivery_note_item/delivery_note_item.txt index 1073f0cdc2..c758fb5604 100644 --- a/stock/doctype/delivery_note_item/delivery_note_item.txt +++ b/stock/doctype/delivery_note_item/delivery_note_item.txt @@ -1,8 +1,8 @@ [ { - "creation": "2013-04-01 10:49:21", + "creation": "2013-04-19 13:31:02", "docstatus": 0, - "modified": "2013-04-17 17:20:58", + "modified": "2013-05-21 16:47:16", "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/utilities/transaction_base.py b/utilities/transaction_base.py index 540b385d51..d4c61f5690 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -297,14 +297,17 @@ def validate_item_fetch(args, item): if not args.company: msgprint(_("Please specify Company"), raise_exception=True) - # validate conversion rates - meta = webnotes.get_doctype(args.doctype) +def validate_currency(args, item, meta=None): + if not meta: + meta = webnotes.get_doctype(args.doctype) + + # validate conversion rate if meta.get_field("currency"): - # validate conversion rate validate_conversion_rate(args.currency, args.conversion_rate, meta.get_label("conversion_rate"), args.company) - # validate price list conversion rate - if 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) \ No newline at end of file + # 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) \ No newline at end of file