diff --git a/accounts/doctype/account/account.py b/accounts/doctype/account/account.py index 4961d72c85a..d4f169295c7 100644 --- a/accounts/doctype/account/account.py +++ b/accounts/doctype/account/account.py @@ -30,6 +30,7 @@ class DocType: self.validate_duplicate_account() self.validate_root_details() self.validate_mandatory() + self.validate_warehouse_account() self.validate_frozen_accounts_modifier() if not self.doc.parent_account: @@ -165,24 +166,24 @@ class DocType: in webnotes.user.get_roles(): return 1 - def check_credit_limit(self, account, company, tot_outstanding): + def check_credit_limit(self, total_outstanding): # Get credit limit credit_limit_from = 'Customer' cr_limit = webnotes.conn.sql("""select t1.credit_limit from tabCustomer t1, `tabAccount` t2 - where t2.name=%s and t1.name = t2.master_name""", account) + where t2.name=%s and t1.name = t2.master_name""", self.doc.name) credit_limit = cr_limit and flt(cr_limit[0][0]) or 0 if not credit_limit: - credit_limit = webnotes.conn.get_value('Company', company, 'credit_limit') - credit_limit_from = 'global settings in the Company' + credit_limit = webnotes.conn.get_value('Company', self.doc.company, 'credit_limit') + credit_limit_from = 'Company' # If outstanding greater than credit limit and not authorized person raise exception - if credit_limit > 0 and flt(tot_outstanding) > credit_limit \ + if credit_limit > 0 and flt(total_outstanding) > credit_limit \ and not self.get_authorized_user(): msgprint("""Total Outstanding amount (%s) for %s can not be \ greater than credit limit (%s). To change your credit limit settings, \ - please update the %s""" % (fmt_money(tot_outstanding), - account, fmt_money(credit_limit), credit_limit_from), raise_exception=1) + please update in the %s master""" % (fmt_money(total_outstanding), + self.doc.name, fmt_money(credit_limit), credit_limit_from), raise_exception=1) def validate_trash(self): """checks gl entries and if child exists""" diff --git a/accounts/doctype/gl_entry/gl_entry.py b/accounts/doctype/gl_entry/gl_entry.py index 9d7367ed2dd..6df97f607ed 100644 --- a/accounts/doctype/gl_entry/gl_entry.py +++ b/accounts/doctype/gl_entry/gl_entry.py @@ -16,7 +16,6 @@ class DocType: self.check_mandatory() self.pl_must_have_cost_center() self.validate_posting_date() - self.check_credit_limit() self.check_pl_account() self.validate_cost_center() @@ -55,21 +54,6 @@ class DocType: from accounts.utils import validate_fiscal_year validate_fiscal_year(self.doc.posting_date, self.doc.fiscal_year, "Posting Date") - def check_credit_limit(self): - master_type, master_name = webnotes.conn.get_value("Account", - self.doc.account, ["master_type", "master_name"]) - - tot_outstanding = 0 #needed when there is no GL Entry in the system for that acc head - if (self.doc.voucher_type=='Journal Voucher' or self.doc.voucher_type=='Sales Invoice') \ - and (master_type =='Customer' and master_name): - dbcr = webnotes.conn.sql("""select sum(debit), sum(credit) from `tabGL Entry` - where account = %s""", self.doc.account) - if dbcr: - tot_outstanding = flt(dbcr[0][0]) - flt(dbcr[0][1]) + \ - flt(self.doc.debit) - flt(self.doc.credit) - get_obj('Account',self.doc.account).check_credit_limit(self.doc.account, - self.doc.company, tot_outstanding) - def check_pl_account(self): if self.doc.is_opening=='Yes' and \ webnotes.conn.get_value("Account", self.doc.account, "is_pl_account") == "Yes": diff --git a/accounts/doctype/journal_voucher/journal_voucher.py b/accounts/doctype/journal_voucher/journal_voucher.py index ade93c658e4..14ec2f75500 100644 --- a/accounts/doctype/journal_voucher/journal_voucher.py +++ b/accounts/doctype/journal_voucher/journal_voucher.py @@ -44,6 +44,7 @@ class DocType(AccountsController): self.check_credit_days() self.check_account_against_entries() self.make_gl_entries() + self.check_credit_limit() def on_cancel(self): from accounts.utils import remove_against_link_from_jv @@ -259,6 +260,13 @@ class DocType(AccountsController): ) if gl_map: make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj) + + def check_credit_limit(self): + for d in self.doclist.get({"parentfield": "entries"}): + master_type, master_name = webnotes.conn.get_value("Account", d.account, + ["master_type", "master_name"]) + if master_type == "Customer" and master_name: + super(DocType, self).check_credit_limit(d.account) def get_balance(self): if not getlist(self.doclist,'entries'): diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py index e183590695a..be7541056a9 100644 --- a/accounts/doctype/sales_invoice/sales_invoice.py +++ b/accounts/doctype/sales_invoice/sales_invoice.py @@ -91,6 +91,7 @@ class DocType(SellingController): # this sequence because outstanding may get -ve self.make_gl_entries() + self.check_credit_limit(self.doc.debit_to) if not cint(self.doc.is_pos) == 1: self.update_against_document_in_jv() @@ -506,32 +507,39 @@ class DocType(SellingController): self.make_sl_entries(sl_entries) - def make_gl_entries(self): - from accounts.general_ledger import make_gl_entries, merge_similar_entries + def make_gl_entries(self, update_gl_entries_after=True): + gl_entries = self.get_gl_entries() + + if gl_entries: + from accounts.general_ledger import make_gl_entries + + update_outstanding = cint(self.doc.is_pos) and self.doc.write_off_account \ + and 'No' or 'Yes' + make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2), + update_outstanding=update_outstanding, merge_entries=False) + + if update_gl_entries_after and cint(self.doc.update_stock) \ + and cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")): + self.update_gl_entries_after() + + def get_gl_entries(self, warehouse_account=None): + from accounts.general_ledger import merge_similar_entries gl_entries = [] self.make_customer_gl_entry(gl_entries) - + self.make_tax_gl_entries(gl_entries) self.make_item_gl_entries(gl_entries) # merge gl entries before adding pos entries gl_entries = merge_similar_entries(gl_entries) - + self.make_pos_gl_entries(gl_entries) - update_outstanding = cint(self.doc.is_pos) and self.doc.write_off_account and 'No' or 'Yes' + return gl_entries - if gl_entries: - make_gl_entries(gl_entries, cancel=(self.doc.docstatus == 2), - update_outstanding=update_outstanding, merge_entries=False) - - if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")) \ - and cint(self.doc.update_stock): - self.update_gl_entries_after() - def make_customer_gl_entry(self, gl_entries): if self.doc.grand_total: gl_entries.append( @@ -575,7 +583,7 @@ class DocType(SellingController): # expense account gl entries if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")) \ and cint(self.doc.update_stock): - gl_entries += self.get_gl_entries_for_stock() + gl_entries += super(DocType, self).get_gl_entries() def make_pos_gl_entries(self, gl_entries): if cint(self.doc.is_pos) and self.doc.cash_bank_account and self.doc.paid_amount: diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py index 9f5b95cd79c..1f0402e4e97 100644 --- a/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -3,7 +3,7 @@ import webnotes import unittest, json -from webnotes.utils import flt, cint +from webnotes.utils import flt from webnotes.model.bean import DocstatusTransitionError, TimestampMismatchError from accounts.utils import get_stock_and_account_difference from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory @@ -364,6 +364,7 @@ class TestSalesInvoice(unittest.TestCase): from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s order by account asc, debit asc""", si.doc.name, as_dict=1) self.assertTrue(gl_entries) + # print gl_entries stock_in_hand = webnotes.conn.get_value("Account", {"master_name": "_Test Warehouse - _TC"}) @@ -382,9 +383,6 @@ class TestSalesInvoice(unittest.TestCase): self.assertEquals(expected_gl_entries[i][1], gle.debit) self.assertEquals(expected_gl_entries[i][2], gle.credit) - - - # cancel si.cancel() gle = webnotes.conn.sql("""select * from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s""", si.doc.name) @@ -395,6 +393,62 @@ class TestSalesInvoice(unittest.TestCase): set_perpetual_inventory(0) + def test_si_gl_entry_with_aii_and_update_stock_with_warehouse_but_no_account(self): + self.clear_stock_account_balance() + set_perpetual_inventory() + webnotes.delete_doc("Account", "_Test Warehouse No Account - _TC") + + # insert purchase receipt + from stock.doctype.purchase_receipt.test_purchase_receipt import test_records \ + as pr_test_records + pr = webnotes.bean(copy=pr_test_records[0]) + pr.doc.naming_series = "_T-Purchase Receipt-" + pr.doclist[1].warehouse = "_Test Warehouse No Account - _TC" + pr.run_method("calculate_taxes_and_totals") + pr.insert() + pr.submit() + + si_doclist = webnotes.copy_doclist(test_records[1]) + si_doclist[0]["update_stock"] = 1 + si_doclist[0]["posting_time"] = "12:05" + si_doclist[1]["warehouse"] = "_Test Warehouse No Account - _TC" + + si = webnotes.bean(copy=si_doclist) + si.insert() + si.submit() + + # check stock ledger entries + sle = webnotes.conn.sql("""select * from `tabStock Ledger Entry` + where voucher_type = 'Sales Invoice' and voucher_no = %s""", + si.doc.name, as_dict=1)[0] + self.assertTrue(sle) + self.assertEquals([sle.item_code, sle.warehouse, sle.actual_qty], + ["_Test Item", "_Test Warehouse No Account - _TC", -1.0]) + + # check gl entries + gl_entries = webnotes.conn.sql("""select account, debit, credit + from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s + order by account asc, debit asc""", si.doc.name, as_dict=1) + self.assertTrue(gl_entries) + + expected_gl_entries = sorted([ + [si.doc.debit_to, 630.0, 0.0], + [si_doclist[1]["income_account"], 0.0, 500.0], + [si_doclist[2]["account_head"], 0.0, 80.0], + [si_doclist[3]["account_head"], 0.0, 50.0], + ]) + for i, gle in enumerate(gl_entries): + self.assertEquals(expected_gl_entries[i][0], gle.account) + self.assertEquals(expected_gl_entries[i][1], gle.debit) + self.assertEquals(expected_gl_entries[i][2], gle.credit) + + si.cancel() + gle = webnotes.conn.sql("""select * from `tabGL Entry` + where voucher_type='Sales Invoice' and voucher_no=%s""", si.doc.name) + + self.assertFalse(gle) + set_perpetual_inventory(0) + def test_sales_invoice_gl_entry_with_aii_no_item_code(self): self.clear_stock_account_balance() set_perpetual_inventory() @@ -599,7 +653,7 @@ class TestSalesInvoice(unittest.TestCase): self._test_recurring_invoice(si7, True) def _test_recurring_invoice(self, base_si, first_and_last_day): - from webnotes.utils import add_months, get_last_day, getdate + from webnotes.utils import add_months, get_last_day from accounts.doctype.sales_invoice.sales_invoice import manage_recurring_invoices no_of_months = ({"Monthly": 1, "Quarterly": 3, "Yearly": 12})[base_si.doc.recurring_type] diff --git a/accounts/general_ledger.py b/accounts/general_ledger.py index b0c585a3a38..9afcf94b2c7 100644 --- a/accounts/general_ledger.py +++ b/accounts/general_ledger.py @@ -3,9 +3,8 @@ from __future__ import unicode_literals import webnotes -from webnotes.utils import flt, cstr, now -from webnotes.model.doc import Document -from webnotes import msgprint, _ +from webnotes.utils import flt, cstr +from webnotes import _ from accounts.utils import validate_expense_against_budget diff --git a/accounts/report/budget_variance_report/budget_variance_report.py b/accounts/report/budget_variance_report/budget_variance_report.py index ee4f6fe20a9..0e241dea1d6 100644 --- a/accounts/report/budget_variance_report/budget_variance_report.py +++ b/accounts/report/budget_variance_report/budget_variance_report.py @@ -16,10 +16,7 @@ def execute(filters=None): period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) cam_map = get_costcenter_account_month_map(filters) - precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2 - data = [] - for cost_center, cost_center_items in cam_map.items(): for account, monthwise_data in cost_center_items.items(): row = [cost_center, account] @@ -29,7 +26,7 @@ def execute(filters=None): for month in relevant_months: month_data = monthwise_data.get(month, {}) for i, fieldname in enumerate(["target", "actual", "variance"]): - value = flt(month_data.get(fieldname), precision) + value = flt(month_data.get(fieldname)) period_data[i] += value totals[i] += value period_data[2] = period_data[0] - period_data[1] @@ -60,7 +57,8 @@ def get_columns(filters): columns.append(label+":Float:120") - return columns + ["Total Target::120", "Total Actual::120", "Total Variance::120"] + return columns + ["Total Target:Float:120", "Total Actual:Float:120", + "Total Variance:Float:120"] #Get cost center & target details def get_costcenter_target_details(filters): @@ -84,14 +82,21 @@ def get_target_distribution_details(filters): #Get actual details from gl entry def get_actual_details(filters): - return webnotes.conn.sql("""select gl.account, gl.debit, gl.credit, + ac_details = webnotes.conn.sql("""select gl.account, gl.debit, gl.credit, gl.cost_center, MONTHNAME(gl.posting_date) as month_name from `tabGL Entry` gl, `tabBudget Detail` bd where gl.fiscal_year=%s and company=%s - and bd.account=gl.account""" % ('%s', '%s'), + and bd.account=gl.account and bd.parent=gl.cost_center""" % ('%s', '%s'), (filters.get("fiscal_year"), filters.get("company")), as_dict=1) + + cc_actual_details = {} + for d in ac_details: + cc_actual_details.setdefault(d.cost_center, {}).setdefault(d.account, []).append(d) + + return cc_actual_details def get_costcenter_account_month_map(filters): + import datetime costcenter_target_details = get_costcenter_target_details(filters) tdd = get_target_distribution_details(filters) actual_details = get_actual_details(filters) @@ -111,11 +116,10 @@ def get_costcenter_account_month_map(filters): month_percentage = ccd.distribution_id and \ tdd.get(ccd.distribution_id, {}).get(month, 0) or 100.0/12 - tav_dict.target = flt(flt(ccd.budget_allocated) * month_percentage /100) - - for ad in actual_details: - if ad.month_name == month and ad.account == ccd.account \ - and ad.cost_center == ccd.name: + tav_dict.target = flt(ccd.budget_allocated) * month_percentage /100 + + for ad in actual_details.get(ccd.name, {}).get(ccd.account, []): + if ad.month_name == month: tav_dict.actual += ad.debit - ad.credit return cam_map \ No newline at end of file diff --git a/buying/doctype/supplier/supplier.js b/buying/doctype/supplier/supplier.js index 1512dec92ea..f258eb7a1d4 100644 --- a/buying/doctype/supplier/supplier.js +++ b/buying/doctype/supplier/supplier.js @@ -3,10 +3,6 @@ wn.require('app/setup/doctype/contact_control/contact_control.js'); -cur_frm.cscript.onload = function(doc,dt,dn){ - -} - cur_frm.cscript.refresh = function(doc,dt,dn) { cur_frm.cscript.make_dashboard(doc); erpnext.hide_naming_series(); @@ -32,7 +28,8 @@ cur_frm.cscript.make_dashboard = function(doc) { cur_frm.dashboard.reset(); if(doc.__islocal) return; - cur_frm.dashboard.set_headline('Loading...') + if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) + cur_frm.dashboard.set_headline('Loading...') cur_frm.dashboard.add_doctype_badge("Supplier Quotation", "supplier"); cur_frm.dashboard.add_doctype_badge("Purchase Order", "supplier"); @@ -46,12 +43,14 @@ cur_frm.cscript.make_dashboard = function(doc) { supplier: cur_frm.doc.name }, callback: function(r) { - cur_frm.dashboard.set_headline( - wn._("Total Billing This Year: ") + "" - + format_currency(r.message.total_billing, cur_frm.doc.default_currency) - + ' / ' + wn._("Unpaid") + ": " - + format_currency(r.message.total_unpaid, cur_frm.doc.default_currency) - + ''); + if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) { + cur_frm.dashboard.set_headline( + wn._("Total Billing This Year: ") + "" + + format_currency(r.message.total_billing, cur_frm.doc.default_currency) + + ' / ' + wn._("Unpaid") + ": " + + format_currency(r.message.total_unpaid, cur_frm.doc.default_currency) + + ''); + } cur_frm.dashboard.set_badge_count(r.message); } }) diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py index 9b2e28b3a9e..906deab85bd 100644 --- a/controllers/accounts_controller.py +++ b/controllers/accounts_controller.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import webnotes from webnotes import _, msgprint from webnotes.utils import flt, cint, today, cstr +from webnotes.model.code import get_obj from setup.utils import get_company_currency from accounts.utils import get_fiscal_year, validate_fiscal_year from utilities.transaction_base import TransactionBase, validate_conversion_rate @@ -423,6 +424,16 @@ class AccountsController(TransactionBase): return self._abbr + def check_credit_limit(self, account): + total_outstanding = webnotes.conn.sql(""" + select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) + from `tabGL Entry` where account = %s""", account) + + total_outstanding = total_outstanding[0][0] if total_outstanding else 0 + if total_outstanding: + get_obj('Account', account).check_credit_limit(total_outstanding) + + @webnotes.whitelist() def get_tax_rate(account_head): - return webnotes.conn.get_value("Account", account_head, "tax_rate") + return webnotes.conn.get_value("Account", account_head, "tax_rate") \ No newline at end of file diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py index 61bd03269c6..896d58b4133 100644 --- a/controllers/selling_controller.py +++ b/controllers/selling_controller.py @@ -247,14 +247,14 @@ class SellingController(StockController): customer_account = webnotes.conn.get_value("Account", {"company": self.doc.company, "master_name": self.doc.customer}, "name") if customer_account: - total_outstanding = 0 total_outstanding = webnotes.conn.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) - from `tabGL Entry` where account = %s""", customer_account)[0][0] + from `tabGL Entry` where account = %s""", customer_account) + total_outstanding = total_outstanding[0][0] if total_outstanding else 0 outstanding_including_current = flt(total_outstanding) + flt(grand_total) webnotes.bean('Account', customer_account).run_method("check_credit_limit", - customer_account, self.doc.company, outstanding_including_current) + outstanding_including_current) def validate_max_discount(self): for d in self.doclist.get({"parentfield": self.fname}): diff --git a/controllers/stock_controller.py b/controllers/stock_controller.py index 359dc9e86ff..e07e752c7e4 100644 --- a/controllers/stock_controller.py +++ b/controllers/stock_controller.py @@ -11,7 +11,7 @@ from controllers.accounts_controller import AccountsController from accounts.general_ledger import make_gl_entries, delete_gl_entries class StockController(AccountsController): - def make_gl_entries(self): + def make_gl_entries(self, update_gl_entries_after=True): if self.doc.docstatus == 2: delete_gl_entries(voucher_type=self.doc.doctype, voucher_no=self.doc.name) @@ -19,12 +19,13 @@ class StockController(AccountsController): warehouse_account = self.get_warehouse_account() if self.doc.docstatus==1: - gl_entries = self.get_gl_entries_for_stock(warehouse_account) + gl_entries = self.get_gl_entries(warehouse_account) make_gl_entries(gl_entries) - self.update_gl_entries_after(warehouse_account) + if update_gl_entries_after: + self.update_gl_entries_after(warehouse_account) - def get_gl_entries_for_stock(self, warehouse_account=None, default_expense_account=None, + def get_gl_entries(self, warehouse_account=None, default_expense_account=None, default_cost_center=None): from accounts.general_ledger import process_gl_map if not warehouse_account: @@ -99,12 +100,10 @@ class StockController(AccountsController): gle = self.get_voucherwise_gl_entries(future_stock_vouchers) if not warehouse_account: warehouse_account = self.get_warehouse_account() - for voucher_type, voucher_no in future_stock_vouchers: existing_gle = gle.get((voucher_type, voucher_no), []) voucher_obj = webnotes.get_obj(voucher_type, voucher_no) - expected_gle = voucher_obj.get_gl_entries_for_stock(warehouse_account) - + expected_gle = voucher_obj.get_gl_entries(warehouse_account) if expected_gle: matched = True if existing_gle: @@ -121,7 +120,7 @@ class StockController(AccountsController): if not matched: self.delete_gl_entries(voucher_type, voucher_no) - make_gl_entries(expected_gle) + voucher_obj.make_gl_entries(update_gl_entries_after=False) else: self.delete_gl_entries(voucher_type, voucher_no) diff --git a/patches/1311/p01_make_gl_entries_for_si.py b/patches/1311/p01_make_gl_entries_for_si.py new file mode 100644 index 00000000000..84e0712b60f --- /dev/null +++ b/patches/1311/p01_make_gl_entries_for_si.py @@ -0,0 +1,13 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. +# License: GNU General Public License v3. See license.txt + +import webnotes + +def execute(): + si_no_gle = webnotes.conn.sql("""select si.name from `tabSales Invoice` si + where docstatus=1 and not exists(select name from `tabGL Entry` + where voucher_type='Sales Invoice' and voucher_no=si.name) + and modified >= '2013-08-01'""") + + for si in si_no_gle: + webnotes.get_obj("Sales Invoice", si[0]).make_gl_entries() \ No newline at end of file diff --git a/patches/patch_list.py b/patches/patch_list.py index d37ed4c8bfd..22322f2c595 100644 --- a/patches/patch_list.py +++ b/patches/patch_list.py @@ -245,4 +245,5 @@ patch_list = [ "execute:webnotes.reload_doc('core', 'doctype', 'defaultvalue') #2013-11-15", "execute:webnotes.reload_doc('core', 'doctype', 'comment') #2013-11-15", "patches.1311.p02_index_singles", + "patches.1311.p01_make_gl_entries_for_si", ] \ No newline at end of file diff --git a/public/js/stock_grid_report.js b/public/js/stock_grid_report.js index 495ea43e405..e87228fde76 100644 --- a/public/js/stock_grid_report.js +++ b/public/js/stock_grid_report.js @@ -25,7 +25,11 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({ add_qty = 0; } } - var value_diff = (rate * add_qty); + if(sl.serial_no) { + var value_diff = this.get_serialized_value_diff(sl); + } else { + var value_diff = (rate * add_qty); + } if(add_qty) wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]); @@ -108,15 +112,8 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({ get_serialized_buying_rates: function() { var serialized_buying_rates = {}; - $.each(wn.report_dump.data["Stock Ledger Entry"], function(i, sle) { - if(sle.qty > 0 && sle.serial_no) { - $.each(sle.serial_no.trim().split("\n"), function(i, sr) { - if(sr && sle.incoming_rate !== undefined - && !serialized_buying_rates[sr.trim().toLowerCase()]) { - serialized_buying_rates[sr.trim().toLowerCase()] = flt(sle.incoming_rate); - } - }); - } + $.each(wn.report_dump.data["Serial No"], function(i, sn) { + serialized_buying_rates[sn.name.toLowerCase()] = flt(sn.incoming_rate); }); return serialized_buying_rates; diff --git a/public/js/transaction.js b/public/js/transaction.js index b6e94521cc0..80082192f53 100644 --- a/public/js/transaction.js +++ b/public/js/transaction.js @@ -496,16 +496,17 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({ this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); var conversion_rate_label = wn.meta.get_label(this.frm.doc.doctype, "conversion_rate", this.frm.doc.name); - - if(this.frm.doc.conversion_rate == 0) { - wn.throw(wn._(conversion_rate_label) + " " + wn._("cannot be 0")); - } - var company_currency = this.get_company_currency(); if(!this.frm.doc.conversion_rate) { - wn.throw(wn._("Please enter valid") + " " + wn._(conversion_rate_label) + - " 1 " + this.frm.doc.currency + " = [?] " + company_currency); + wn.throw(repl('%(conversion_rate_label)s' + + wn._(' is mandatory. Maybe Currency Exchange record is not created for ') + + '%(from_currency)s' + wn._(" to ") + '%(to_currency)s', + { + "conversion_rate_label": conversion_rate_label, + "from_currency": this.frm.doc.currency, + "to_currency": company_currency + })); } }, diff --git a/selling/doctype/customer/customer.js b/selling/doctype/customer/customer.js index 0828adf008a..ea1e93d43bf 100644 --- a/selling/doctype/customer/customer.js +++ b/selling/doctype/customer/customer.js @@ -41,7 +41,8 @@ cur_frm.cscript.setup_dashboard = function(doc) { cur_frm.dashboard.reset(doc); if(doc.__islocal) return; - cur_frm.dashboard.set_headline(''+ wn._('Loading...')+ '') + if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) + cur_frm.dashboard.set_headline(''+ wn._('Loading...')+ '') cur_frm.dashboard.add_doctype_badge("Opportunity", "customer"); cur_frm.dashboard.add_doctype_badge("Quotation", "customer"); @@ -56,12 +57,14 @@ cur_frm.cscript.setup_dashboard = function(doc) { customer: cur_frm.doc.name }, callback: function(r) { - cur_frm.dashboard.set_headline( - wn._("Total Billing This Year: ") + "" - + format_currency(r.message.total_billing, cur_frm.doc.default_currency) - + ' / ' + wn._("Unpaid") + ": " - + format_currency(r.message.total_unpaid, cur_frm.doc.default_currency) - + ''); + if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) { + cur_frm.dashboard.set_headline( + wn._("Total Billing This Year: ") + "" + + format_currency(r.message.total_billing, cur_frm.doc.default_currency) + + ' / ' + wn._("Unpaid") + ": " + + format_currency(r.message.total_unpaid, cur_frm.doc.default_currency) + + ''); + } cur_frm.dashboard.set_badge_count(r.message); } }) diff --git a/selling/doctype/sales_common/sales_common.py b/selling/doctype/sales_common/sales_common.py new file mode 100644 index 00000000000..3be27b225fb --- /dev/null +++ b/selling/doctype/sales_common/sales_common.py @@ -0,0 +1,343 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import webnotes + +from webnotes.utils import cint, cstr, flt +from webnotes.model.doc import addchild +from webnotes.model.bean import getlist +from webnotes.model.code import get_obj +from webnotes import msgprint, _ + +from utilities.transaction_base import TransactionBase + +class DocType(TransactionBase): + def __init__(self,d,dl): + self.doc, self.doclist = d, dl + + + def get_contact_details(self, obj = '', primary = 0): + cond = " and contact_name = '"+cstr(obj.doc.contact_person)+"'" + if primary: cond = " and is_primary_contact = 'Yes'" + contact = webnotes.conn.sql("select contact_name, contact_no, email_id, contact_address from `tabContact` where customer = '%s' and docstatus != 2 %s" %(obj.doc.customer, cond), as_dict = 1) + if not contact: + return + c = contact[0] + obj.doc.contact_person = c['contact_name'] or '' + obj.doc.contact_no = c['contact_no'] or '' + obj.doc.email_id = c['email_id'] or '' + obj.doc.customer_mobile_no = c['contact_no'] or '' + if c['contact_address']: + obj.doc.customer_address = c['contact_address'] + + def get_invoice_details(self, obj = ''): + if obj.doc.company: + acc_head = webnotes.conn.sql("select name from `tabAccount` where name = '%s' and docstatus != 2" % (cstr(obj.doc.customer) + " - " + webnotes.conn.get_value('Company', obj.doc.company, 'abbr'))) + obj.doc.debit_to = acc_head and acc_head[0][0] or '' + + def get_tax_details(self, item_code, obj): + import json + tax = webnotes.conn.sql("select tax_type, tax_rate from `tabItem Tax` where parent = %s" , item_code) + t = {} + for x in tax: t[x[0]] = flt(x[1]) + ret = { + 'item_tax_rate' : tax and json.dumps(t) or '' + } + return ret + + def get_serial_details(self, serial_no, obj): + import json + item = webnotes.conn.sql("select item_code, make, label,brand, description from `tabSerial No` where name = '%s' and docstatus != 2" %(serial_no), as_dict=1) + tax = webnotes.conn.sql("select tax_type, tax_rate from `tabItem Tax` where parent = %s" , item[0]['item_code']) + t = {} + for x in tax: t[x[0]] = flt(x[1]) + ret = { + 'item_code' : item and item[0]['item_code'] or '', + 'make' : item and item[0]['make'] or '', + 'label' : item and item[0]['label'] or '', + 'brand' : item and item[0]['brand'] or '', + 'description' : item and item[0]['description'] or '', + 'item_tax_rate' : json.dumps(t) + } + return ret + + # To verify whether rate entered in details table does not exceed max discount % + # ======================================================================================= + def validate_max_discount(self,obj, detail_table): + for d in getlist(obj.doclist, detail_table): + discount = webnotes.conn.sql("select max_discount from tabItem where name = '%s'" %(d.item_code),as_dict = 1) + 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 Tax rate if account type is TAX + # ========================================================================= + def get_rate(self, arg): + arg = eval(arg) + rate = webnotes.conn.sql("select account_type, tax_rate from `tabAccount` where name = '%s' and docstatus != 2" %(arg['account_head']), as_dict=1) + ret = {'rate' : 0} + if arg['charge_type'] == 'Actual' and rate[0]['account_type'] == 'Tax': + msgprint("You cannot select ACCOUNT HEAD of type TAX as your CHARGE TYPE is 'ACTUAL'") + ret = { + 'account_head' : '' + } + elif rate[0]['account_type'] in ['Tax', 'Chargeable'] and not arg['charge_type'] == 'Actual': + ret = { + 'rate' : rate and flt(rate[0]['tax_rate']) or 0 + } + return ret + + + def get_item_list(self, obj, is_stopped=0): + """get item list""" + il = [] + for d in getlist(obj.doclist, obj.fname): + reserved_warehouse = "" + reserved_qty_for_main_item = 0 + + if obj.doc.doctype == "Sales Order": + if (webnotes.conn.get_value("Item", d.item_code, "is_stock_item") == 'Yes' or + self.has_sales_bom(d.item_code)) and not d.reserved_warehouse: + webnotes.throw(_("Please enter Reserved Warehouse for item ") + + d.item_code + _(" as it is stock Item or packing item")) + reserved_warehouse = d.reserved_warehouse + if flt(d.qty) > flt(d.delivered_qty): + reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty) + + if obj.doc.doctype == "Delivery Note" and d.prevdoc_doctype == 'Sales Order': + # if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12. + # But in this case reserved qty should only be reduced by 10 and not 12 + + already_delivered_qty = self.get_already_delivered_qty(obj.doc.name, + d.prevdoc_docname, d.prevdoc_detail_docname) + so_qty, reserved_warehouse = self.get_so_qty_and_warehouse(d.prevdoc_detail_docname) + + if already_delivered_qty + d.qty > so_qty: + reserved_qty_for_main_item = -(so_qty - already_delivered_qty) + else: + reserved_qty_for_main_item = -flt(d.qty) + + if self.has_sales_bom(d.item_code): + for p in getlist(obj.doclist, 'packing_details'): + if p.parent_detail_docname == d.name and p.parent_item == d.item_code: + # the packing details table's qty is already multiplied with parent's qty + il.append(webnotes._dict({ + 'warehouse': p.warehouse, + 'reserved_warehouse': reserved_warehouse, + 'item_code': p.item_code, + 'qty': flt(p.qty), + 'reserved_qty': (flt(p.qty)/flt(d.qty)) * reserved_qty_for_main_item, + 'uom': p.uom, + 'batch_no': cstr(p.batch_no).strip(), + 'serial_no': cstr(p.serial_no).strip(), + 'name': d.name + })) + else: + il.append(webnotes._dict({ + 'warehouse': d.warehouse, + 'reserved_warehouse': reserved_warehouse, + 'item_code': d.item_code, + 'qty': d.qty, + 'reserved_qty': reserved_qty_for_main_item, + 'uom': d.stock_uom, + 'batch_no': cstr(d.batch_no).strip(), + 'serial_no': cstr(d.serial_no).strip(), + 'name': d.name + })) + return il + + def get_already_delivered_qty(self, dn, so, so_detail): + qty = webnotes.conn.sql("""select sum(qty) from `tabDelivery Note Item` + where prevdoc_detail_docname = %s and docstatus = 1 + and prevdoc_doctype = 'Sales Order' and prevdoc_docname = %s + and parent != %s""", (so_detail, so, dn)) + return qty and flt(qty[0][0]) or 0.0 + + def get_so_qty_and_warehouse(self, so_detail): + so_item = webnotes.conn.sql("""select qty, reserved_warehouse from `tabSales Order Item` + where name = %s and docstatus = 1""", so_detail, as_dict=1) + so_qty = so_item and flt(so_item[0]["qty"]) or 0.0 + so_warehouse = so_item and so_item[0]["reserved_warehouse"] or "" + return so_qty, so_warehouse + + def has_sales_bom(self, item_code): + return webnotes.conn.sql("select name from `tabSales BOM` where new_item_code=%s and docstatus != 2", item_code) + + def get_sales_bom_items(self, item_code): + return webnotes.conn.sql("""select t1.item_code, t1.qty, t1.uom + from `tabSales BOM Item` t1, `tabSales BOM` t2 + where t2.new_item_code=%s and t1.parent = t2.name""", item_code, as_dict=1) + + def get_packing_item_details(self, item): + return webnotes.conn.sql("select item_name, description, stock_uom from `tabItem` where name = %s", item, as_dict = 1)[0] + + def get_bin_qty(self, item, warehouse): + det = webnotes.conn.sql("select actual_qty, projected_qty from `tabBin` where item_code = '%s' and warehouse = '%s'" % (item, warehouse), as_dict = 1) + return det and det[0] or '' + + def update_packing_list_item(self,obj, packing_item_code, qty, warehouse, line): + bin = self.get_bin_qty(packing_item_code, warehouse) + item = self.get_packing_item_details(packing_item_code) + + # check if exists + exists = 0 + for d in getlist(obj.doclist, 'packing_details'): + if d.parent_item == line.item_code and d.item_code == packing_item_code and d.parent_detail_docname == line.name: + pi, exists = d, 1 + break + + if not exists: + pi = addchild(obj.doc, 'packing_details', 'Delivery Note Packing Item', + obj.doclist) + + pi.parent_item = line.item_code + pi.item_code = packing_item_code + pi.item_name = item['item_name'] + pi.parent_detail_docname = line.name + pi.description = item['description'] + pi.uom = item['stock_uom'] + pi.qty = flt(qty) + pi.actual_qty = bin and flt(bin['actual_qty']) or 0 + pi.projected_qty = bin and flt(bin['projected_qty']) or 0 + pi.prevdoc_doctype = line.prevdoc_doctype + if not pi.warehouse: + pi.warehouse = warehouse + if not pi.batch_no: + pi.batch_no = cstr(line.batch_no) + pi.idx = self.packing_list_idx + + # saved, since this function is called on_update of delivery note + pi.save() + + self.packing_list_idx += 1 + + + def make_packing_list(self, obj, fname): + """make packing list for sales bom item""" + self.packing_list_idx = 0 + parent_items = [] + for d in getlist(obj.doclist, fname): + warehouse = fname == "sales_order_details" and d.reserved_warehouse or d.warehouse + if self.has_sales_bom(d.item_code): + for i in self.get_sales_bom_items(d.item_code): + self.update_packing_list_item(obj, i['item_code'], flt(i['qty'])*flt(d.qty), warehouse, d) + + if [d.item_code, d.name] not in parent_items: + parent_items.append([d.item_code, d.name]) + + obj.doclist = self.cleanup_packing_list(obj, parent_items) + + return obj.doclist + + def cleanup_packing_list(self, obj, parent_items): + """Remove all those child items which are no longer present in main item table""" + delete_list = [] + for d in getlist(obj.doclist, 'packing_details'): + if [d.parent_item, d.parent_detail_docname] not in parent_items: + # mark for deletion from doclist + delete_list.append(d.name) + + if not delete_list: + return obj.doclist + + # delete from doclist + obj.doclist = webnotes.doclist(filter(lambda d: d.name not in delete_list, obj.doclist)) + + # delete from db + webnotes.conn.sql("""\ + delete from `tabDelivery Note Packing Item` + where name in (%s)""" + % (", ".join(["%s"] * len(delete_list))), + tuple(delete_list)) + + return obj.doclist + + + def get_month(self,date): + """Get month based on date (required in sales person and sales partner)""" + month_list = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'] + month_idx = cint(cstr(date).split('-')[1])-1 + return month_list[month_idx] + + + # **** Check for Stop SO as no transactions can be made against Stopped SO. Need to unstop it. *** + def check_stop_sales_order(self,obj): + for d in getlist(obj.doclist,obj.fname): + ref_doc_name = '' + if d.fields.has_key('prevdoc_docname') and d.prevdoc_docname and d.prevdoc_doctype == 'Sales Order': + ref_doc_name = d.prevdoc_docname + elif d.fields.has_key('sales_order') and d.sales_order and not d.delivery_note: + ref_doc_name = d.sales_order + if ref_doc_name: + so_status = webnotes.conn.sql("select status from `tabSales Order` where name = %s",ref_doc_name) + so_status = so_status and so_status[0][0] or '' + if so_status == 'Stopped': + msgprint("You cannot do any transaction against Sales Order : '%s' as it is Stopped." %(ref_doc_name)) + raise Exception + + def check_active_sales_items(self,obj): + for d in getlist(obj.doclist, obj.fname): + if d.item_code: + item = webnotes.conn.sql("""select docstatus, is_sales_item, + is_service_item, default_income_account from tabItem where name = %s""", + d.item_code, as_dict=True)[0] + if item.is_sales_item == 'No' and item.is_service_item == 'No': + msgprint("Item : '%s' is neither Sales nor Service Item" % (d.item_code)) + raise Exception + if d.income_account and not item.default_income_account: + webnotes.conn.set_value("Item", d.item_code, "default_income_account", d.income_account) + + + def check_credit(self,obj,grand_total): + acc_head = webnotes.conn.sql("select name from `tabAccount` where company = '%s' and master_name = '%s'"%(obj.doc.company, obj.doc.customer)) + if acc_head: + dbcr = webnotes.conn.sql("""select sum(debit), sum(credit) from `tabGL Entry` + where account = %s""", acc_head[0][0]) + tot_outstanding = flt(dbcr[0][0])-flt(dbcr[0][1]) if dbcr else 0 + + exact_outstanding = flt(tot_outstanding) + flt(grand_total) + get_obj('Account',acc_head[0][0]).check_credit_limit(exact_outstanding) + + def get_prevdoc_date(self, obj): + for d in getlist(obj.doclist, obj.fname): + if d.prevdoc_doctype and d.prevdoc_docname: + if d.prevdoc_doctype in ["Sales Invoice", "Delivery Note"]: + date_field = "posting_date" + else: + date_field = "transaction_date" + + d.prevdoc_date = webnotes.conn.get_value(d.prevdoc_doctype, + d.prevdoc_docname, date_field) + +def get_batch_no(doctype, txt, searchfield, start, page_len, filters): + from controllers.queries import get_match_cond + + if filters.has_key('warehouse'): + return webnotes.conn.sql("""select batch_no from `tabStock Ledger Entry` sle + where item_code = '%(item_code)s' + and warehouse = '%(warehouse)s' + and batch_no like '%(txt)s' + and exists(select * from `tabBatch` + where name = sle.batch_no + and (ifnull(expiry_date, '')='' or expiry_date >= '%(posting_date)s') + and docstatus != 2) + %(mcond)s + group by batch_no having sum(actual_qty) > 0 + order by batch_no desc + limit %(start)s, %(page_len)s """ % {'item_code': filters['item_code'], + 'warehouse': filters['warehouse'], 'posting_date': filters['posting_date'], + 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype, searchfield), + 'start': start, 'page_len': page_len}) + else: + return webnotes.conn.sql("""select name from tabBatch + where docstatus != 2 + and item = '%(item_code)s' + and (ifnull(expiry_date, '')='' or expiry_date >= '%(posting_date)s') + and name like '%(txt)s' + %(mcond)s + order by name desc + limit %(start)s, %(page_len)s""" % {'item_code': filters['item_code'], + 'posting_date': filters['posting_date'], 'txt': "%%%s%%" % txt, + 'mcond':get_match_cond(doctype, searchfield),'start': start, + 'page_len': page_len}) diff --git a/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py b/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py index 6fdafe083d7..ebb2d9f96fe 100644 --- a/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py +++ b/selling/report/sales_person_target_variance_item_group_wise/sales_person_target_variance_item_group_wise.py @@ -58,7 +58,8 @@ def get_columns(filters): columns.append(label+":Float:120") - return columns + ["Total Target::120", "Total Achieved::120", "Total Variance::120"] + return columns + ["Total Target:Float:120", "Total Achieved:Float:120", + "Total Variance:Float:120"] #Get sales person & item group details def get_salesperson_details(filters): @@ -83,7 +84,7 @@ def get_target_distribution_details(filters): def get_achieved_details(filters): start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:] - return webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, + item_details = webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, st.sales_person, MONTHNAME(so.transaction_date) as month_name from `tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st where soi.parent=so.name and so.docstatus=1 and @@ -91,6 +92,13 @@ def get_achieved_details(filters): so.transaction_date<=%s""" % ('%s', '%s'), (start_date, end_date), as_dict=1) + item_actual_details = {} + for d in item_details: + item_actual_details.setdefault(d.sales_person, {}).setdefault(\ + get_item_group(d.item_code), []).append(d) + + return item_actual_details + def get_salesperson_item_month_map(filters): import datetime salesperson_details = get_salesperson_details(filters) @@ -110,17 +118,15 @@ def get_salesperson_item_month_map(filters): month_percentage = sd.distribution_id and \ tdd.get(sd.distribution_id, {}).get(month, 0) or 100.0/12 - for ad in achieved_details: + for ad in achieved_details.get(sd.name, {}).get(sd.item_group, []): if (filters["target_on"] == "Quantity"): - tav_dict.target = flt(flt(sd.target_qty) * month_percentage/100, 2) - if ad.month_name == month and get_item_group(ad.item_code) == sd.item_group \ - and ad.sales_person == sd.name: + tav_dict.target = flt(sd.target_qty) * month_percentage / 100 + if ad.month_name == month: tav_dict.achieved += ad.qty if (filters["target_on"] == "Amount"): - tav_dict.target = flt(flt(sd.target_amount) * month_percentage/100, 2) - if ad.month_name == month and get_item_group(ad.item_code) == sd.item_group \ - and ad.sales_person == sd.name: + tav_dict.target = flt(sd.target_amount) * month_percentage / 100 + if ad.month_name == month: tav_dict.achieved += ad.amount return sim_map diff --git a/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py b/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py index 829f7818e32..2aafe3c6bdc 100644 --- a/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py +++ b/selling/report/territory_target_variance_item_group_wise/territory_target_variance_item_group_wise.py @@ -15,11 +15,8 @@ def execute(filters=None): columns = get_columns(filters) period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) tim_map = get_territory_item_month_map(filters) - - precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2 - + data = [] - for territory, territory_items in tim_map.items(): for item_group, monthwise_data in territory_items.items(): row = [territory, item_group] @@ -29,7 +26,7 @@ def execute(filters=None): for month in relevant_months: month_data = monthwise_data.get(month, {}) for i, fieldname in enumerate(["target", "achieved", "variance"]): - value = flt(month_data.get(fieldname), precision) + value = flt(month_data.get(fieldname)) period_data[i] += value totals[i] += value period_data[2] = period_data[0] - period_data[1] @@ -58,7 +55,8 @@ def get_columns(filters): label = label % from_date.strftime("%b") columns.append(label+":Float:120") - return columns + ["Total Target::120", "Total Achieved::120", "Total Variance::120"] + return columns + ["Total Target:Float:120", "Total Achieved:Float:120", + "Total Variance:Float:120"] #Get territory & item group details def get_territory_details(filters): @@ -83,14 +81,22 @@ def get_target_distribution_details(filters): def get_achieved_details(filters): start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:] - return webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, + item_details = webnotes.conn.sql("""select soi.item_code, soi.qty, soi.amount, so.transaction_date, so.territory, MONTHNAME(so.transaction_date) as month_name from `tabSales Order Item` soi, `tabSales Order` so where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and so.transaction_date<=%s""" % ('%s', '%s'), (start_date, end_date), as_dict=1) + item_actual_details = {} + for d in item_details: + item_actual_details.setdefault(d.territory, {}).setdefault(\ + get_item_group(d.item_code), []).append(d) + + return item_actual_details + def get_territory_item_month_map(filters): + import datetime territory_details = get_territory_details(filters) tdd = get_target_distribution_details(filters) achieved_details = get_achieved_details(filters) @@ -110,17 +116,15 @@ def get_territory_item_month_map(filters): month_percentage = td.distribution_id and \ tdd.get(td.distribution_id, {}).get(month, 0) or 100.0/12 - for ad in achieved_details: + for ad in achieved_details.get(td.name, {}).get(td.item_group, []): if (filters["target_on"] == "Quantity"): - tav_dict.target = flt(flt(td.target_qty) * month_percentage /100) - if ad.month_name == month and get_item_group(ad.item_code) == td.item_group \ - and ad.territory == td.name: + tav_dict.target = flt(td.target_qty) * month_percentage / 100 + if ad.month_name == month: tav_dict.achieved += ad.qty if (filters["target_on"] == "Amount"): - tav_dict.target = flt(flt(td.target_amount) * month_percentage / 100) - if ad.month_name == month and get_item_group(ad.item_code) == td.item_group \ - and ad.territory == td.name: + tav_dict.target = flt(td.target_amount) * month_percentage / 100 + if ad.month_name == month: tav_dict.achieved += ad.amount return tim_map diff --git a/startup/report_data_map.py b/startup/report_data_map.py index 54453f68bbd..12ceae421a2 100644 --- a/startup/report_data_map.py +++ b/startup/report_data_map.py @@ -88,6 +88,11 @@ data_map = { }, "force_index": "posting_sort_index" }, + "Serial No": { + "columns": ["name", "purchase_rate as incoming_rate"], + "conditions": ["docstatus < 2"], + "order_by": "name" + }, "Stock Entry": { "columns": ["name", "purpose"], "conditions": ["docstatus=1"], diff --git a/stock/doctype/purchase_receipt/purchase_receipt.py b/stock/doctype/purchase_receipt/purchase_receipt.py index 99cbd0be9c3..df774bc543f 100644 --- a/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/stock/doctype/purchase_receipt/purchase_receipt.py @@ -284,11 +284,13 @@ class DocType(BuyingController): bin = webnotes.conn.sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.rm_item_code, self.doc.supplier_warehouse), as_dict = 1) d.current_stock = bin and flt(bin[0]['actual_qty']) or 0 - def get_gl_entries_for_stock(self, warehouse_account=None): + def get_rate(self,arg): + return get_obj('Purchase Common').get_rate(arg,self) + + def get_gl_entries(self, warehouse_account=None): against_stock_account = self.get_company_default("stock_received_but_not_billed") - gl_entries = super(DocType, self).get_gl_entries_for_stock(warehouse_account, - against_stock_account) + gl_entries = super(DocType, self).get_gl_entries(warehouse_account, against_stock_account) return gl_entries diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py index 26759af003c..df1814edd27 100644 --- a/stock/doctype/stock_entry/stock_entry.py +++ b/stock/doctype/stock_entry/stock_entry.py @@ -475,7 +475,7 @@ class DocType(StockController): self.doc.from_warehouse = "" item = webnotes.conn.sql("""select name, item_name, description, - uom, purchase_account, cost_center from `tabItem` + stock_uom, purchase_account, cost_center from `tabItem` where name=(select item from tabBOM where name=%s)""", self.doc.bom_no, as_dict=1) self.add_to_stock_entry_detail({ @@ -483,7 +483,7 @@ class DocType(StockController): "qty": self.doc.fg_completed_qty, "item_name": item[0].item_name, "description": item[0]["description"], - "stock_uom": item[0]["uom"], + "stock_uom": item[0]["stock_uom"], "from_warehouse": "", "expense_account": item[0].purchase_account, "cost_center": item[0].cost_center, diff --git a/stock/doctype/stock_reconciliation/stock_reconciliation.py b/stock/doctype/stock_reconciliation/stock_reconciliation.py index 9feb57e716b..22d011765ff 100644 --- a/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -275,11 +275,11 @@ class DocType(StockController): "posting_time": self.doc.posting_time }) - def get_gl_entries_for_stock(self, warehouse_account=None): + def get_gl_entries(self, warehouse_account=None): if not self.doc.cost_center: msgprint(_("Please enter Cost Center"), raise_exception=1) - return super(DocType, self).get_gl_entries_for_stock(warehouse_account, + return super(DocType, self).get_gl_entries(warehouse_account, self.doc.expense_account, self.doc.cost_center) diff --git a/stock/doctype/warehouse/test_warehouse.py b/stock/doctype/warehouse/test_warehouse.py index 76b18180e21..43b260011c6 100644 --- a/stock/doctype/warehouse/test_warehouse.py +++ b/stock/doctype/warehouse/test_warehouse.py @@ -23,5 +23,10 @@ test_records = [ "doctype": "Warehouse User", "parentfield": "warehouse_users", "user": "test2@example.com" - }] + }], + [{ + "doctype": "Warehouse", + "warehouse_name": "_Test Warehouse No Account", + "company": "_Test Company", + }], ] diff --git a/stock/page/stock_balance/stock_balance.js b/stock/page/stock_balance/stock_balance.js index caa0c9e58b3..4814d74c10b 100644 --- a/stock/page/stock_balance/stock_balance.js +++ b/stock/page/stock_balance/stock_balance.js @@ -22,7 +22,7 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({ this._super(wrapper, { title: wn._("Stock Balance"), doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand", - "Stock Entry", "Project"], + "Stock Entry", "Project", "Serial No"], }); }, setup_columns: function() { diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py index 0f4d6bcacd5..684da467951 100644 --- a/utilities/transaction_base.py +++ b/utilities/transaction_base.py @@ -426,15 +426,15 @@ def get_address_territory(address_doc): def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company): """common validation for currency and price list currency""" - if conversion_rate == 0: - msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True) - company_currency = webnotes.conn.get_value("Company", company, "default_currency") - + if not conversion_rate: - msgprint(_('Please enter valid ') + conversion_rate_label + (': ') - + ("1 %s = [?] %s" % (currency, company_currency)), - raise_exception=True) + msgprint(_('%(conversion_rate_label)s is mandatory. Maybe Currency Exchange \ + record is not created for %(from_currency)s to %(to_currency)s') % { + "conversion_rate_label": conversion_rate_label, + "from_currency": currency, + "to_currency": company_currency + }, raise_exception=True) def validate_item_fetch(args, item): from stock.utils import validate_end_of_life