Merge branch '1310' of github.com:webnotes/erpnext

Conflicts:
	accounts/doctype/account/account.py
	controllers/accounts_controller.py
	patches/patch_list.py
	selling/doctype/sales_common/sales_common.py
	stock/doctype/purchase_receipt/purchase_receipt.py
This commit is contained in:
Anand Doshi 2013-11-15 19:12:09 +05:30
commit 0c26f8116d
26 changed files with 590 additions and 143 deletions

View File

@ -30,6 +30,7 @@ class DocType:
self.validate_duplicate_account() self.validate_duplicate_account()
self.validate_root_details() self.validate_root_details()
self.validate_mandatory() self.validate_mandatory()
self.validate_warehouse_account()
self.validate_frozen_accounts_modifier() self.validate_frozen_accounts_modifier()
if not self.doc.parent_account: if not self.doc.parent_account:
@ -165,24 +166,24 @@ class DocType:
in webnotes.user.get_roles(): in webnotes.user.get_roles():
return 1 return 1
def check_credit_limit(self, account, company, tot_outstanding): def check_credit_limit(self, total_outstanding):
# Get credit limit # Get credit limit
credit_limit_from = 'Customer' credit_limit_from = 'Customer'
cr_limit = webnotes.conn.sql("""select t1.credit_limit from tabCustomer t1, `tabAccount` t2 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 credit_limit = cr_limit and flt(cr_limit[0][0]) or 0
if not credit_limit: if not credit_limit:
credit_limit = webnotes.conn.get_value('Company', company, 'credit_limit') credit_limit = webnotes.conn.get_value('Company', self.doc.company, 'credit_limit')
credit_limit_from = 'global settings in the Company' credit_limit_from = 'Company'
# If outstanding greater than credit limit and not authorized person raise exception # 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(): and not self.get_authorized_user():
msgprint("""Total Outstanding amount (%s) for <b>%s</b> can not be \ msgprint("""Total Outstanding amount (%s) for <b>%s</b> can not be \
greater than credit limit (%s). To change your credit limit settings, \ greater than credit limit (%s). To change your credit limit settings, \
please update the <b>%s</b>""" % (fmt_money(tot_outstanding), please update in the <b>%s</b> master""" % (fmt_money(total_outstanding),
account, fmt_money(credit_limit), credit_limit_from), raise_exception=1) self.doc.name, fmt_money(credit_limit), credit_limit_from), raise_exception=1)
def validate_trash(self): def validate_trash(self):
"""checks gl entries and if child exists""" """checks gl entries and if child exists"""

View File

@ -16,7 +16,6 @@ class DocType:
self.check_mandatory() self.check_mandatory()
self.pl_must_have_cost_center() self.pl_must_have_cost_center()
self.validate_posting_date() self.validate_posting_date()
self.check_credit_limit()
self.check_pl_account() self.check_pl_account()
self.validate_cost_center() self.validate_cost_center()
@ -55,21 +54,6 @@ class DocType:
from accounts.utils import validate_fiscal_year from accounts.utils import validate_fiscal_year
validate_fiscal_year(self.doc.posting_date, self.doc.fiscal_year, "Posting Date") 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): def check_pl_account(self):
if self.doc.is_opening=='Yes' and \ if self.doc.is_opening=='Yes' and \
webnotes.conn.get_value("Account", self.doc.account, "is_pl_account") == "Yes": webnotes.conn.get_value("Account", self.doc.account, "is_pl_account") == "Yes":

View File

@ -44,6 +44,7 @@ class DocType(AccountsController):
self.check_credit_days() self.check_credit_days()
self.check_account_against_entries() self.check_account_against_entries()
self.make_gl_entries() self.make_gl_entries()
self.check_credit_limit()
def on_cancel(self): def on_cancel(self):
from accounts.utils import remove_against_link_from_jv from accounts.utils import remove_against_link_from_jv
@ -260,6 +261,13 @@ class DocType(AccountsController):
if gl_map: if gl_map:
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj) 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): def get_balance(self):
if not getlist(self.doclist,'entries'): if not getlist(self.doclist,'entries'):
msgprint("Please enter atleast 1 entry in 'GL Entries' table") msgprint("Please enter atleast 1 entry in 'GL Entries' table")

View File

@ -91,6 +91,7 @@ class DocType(SellingController):
# this sequence because outstanding may get -ve # this sequence because outstanding may get -ve
self.make_gl_entries() self.make_gl_entries()
self.check_credit_limit(self.doc.debit_to)
if not cint(self.doc.is_pos) == 1: if not cint(self.doc.is_pos) == 1:
self.update_against_document_in_jv() self.update_against_document_in_jv()
@ -506,8 +507,23 @@ class DocType(SellingController):
self.make_sl_entries(sl_entries) self.make_sl_entries(sl_entries)
def make_gl_entries(self): def make_gl_entries(self, update_gl_entries_after=True):
from accounts.general_ledger import make_gl_entries, merge_similar_entries 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 = [] gl_entries = []
@ -522,15 +538,7 @@ class DocType(SellingController):
self.make_pos_gl_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): def make_customer_gl_entry(self, gl_entries):
if self.doc.grand_total: if self.doc.grand_total:
@ -575,7 +583,7 @@ class DocType(SellingController):
# expense account gl entries # expense account gl entries
if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")) \ if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")) \
and cint(self.doc.update_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): 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: if cint(self.doc.is_pos) and self.doc.cash_bank_account and self.doc.paid_amount:

View File

@ -3,7 +3,7 @@
import webnotes import webnotes
import unittest, json import unittest, json
from webnotes.utils import flt, cint from webnotes.utils import flt
from webnotes.model.bean import DocstatusTransitionError, TimestampMismatchError from webnotes.model.bean import DocstatusTransitionError, TimestampMismatchError
from accounts.utils import get_stock_and_account_difference from accounts.utils import get_stock_and_account_difference
from stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory 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 from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc, debit asc""", si.doc.name, as_dict=1) order by account asc, debit asc""", si.doc.name, as_dict=1)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
# print gl_entries
stock_in_hand = webnotes.conn.get_value("Account", {"master_name": "_Test Warehouse - _TC"}) 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][1], gle.debit)
self.assertEquals(expected_gl_entries[i][2], gle.credit) self.assertEquals(expected_gl_entries[i][2], gle.credit)
# cancel
si.cancel() si.cancel()
gle = webnotes.conn.sql("""select * from `tabGL Entry` gle = webnotes.conn.sql("""select * from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.doc.name) where voucher_type='Sales Invoice' and voucher_no=%s""", si.doc.name)
@ -395,6 +393,62 @@ class TestSalesInvoice(unittest.TestCase):
set_perpetual_inventory(0) 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): def test_sales_invoice_gl_entry_with_aii_no_item_code(self):
self.clear_stock_account_balance() self.clear_stock_account_balance()
set_perpetual_inventory() set_perpetual_inventory()
@ -599,7 +653,7 @@ class TestSalesInvoice(unittest.TestCase):
self._test_recurring_invoice(si7, True) self._test_recurring_invoice(si7, True)
def _test_recurring_invoice(self, base_si, first_and_last_day): 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 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] no_of_months = ({"Monthly": 1, "Quarterly": 3, "Yearly": 12})[base_si.doc.recurring_type]

View File

@ -3,9 +3,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import webnotes import webnotes
from webnotes.utils import flt, cstr, now from webnotes.utils import flt, cstr
from webnotes.model.doc import Document from webnotes import _
from webnotes import msgprint, _
from accounts.utils import validate_expense_against_budget from accounts.utils import validate_expense_against_budget

View File

@ -16,10 +16,7 @@ def execute(filters=None):
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
cam_map = get_costcenter_account_month_map(filters) cam_map = get_costcenter_account_month_map(filters)
precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2
data = [] data = []
for cost_center, cost_center_items in cam_map.items(): for cost_center, cost_center_items in cam_map.items():
for account, monthwise_data in cost_center_items.items(): for account, monthwise_data in cost_center_items.items():
row = [cost_center, account] row = [cost_center, account]
@ -29,7 +26,7 @@ def execute(filters=None):
for month in relevant_months: for month in relevant_months:
month_data = monthwise_data.get(month, {}) month_data = monthwise_data.get(month, {})
for i, fieldname in enumerate(["target", "actual", "variance"]): 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 period_data[i] += value
totals[i] += value totals[i] += value
period_data[2] = period_data[0] - period_data[1] period_data[2] = period_data[0] - period_data[1]
@ -60,7 +57,8 @@ def get_columns(filters):
columns.append(label+":Float:120") 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 #Get cost center & target details
def get_costcenter_target_details(filters): def get_costcenter_target_details(filters):
@ -84,14 +82,21 @@ def get_target_distribution_details(filters):
#Get actual details from gl entry #Get actual details from gl entry
def get_actual_details(filters): 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 gl.cost_center, MONTHNAME(gl.posting_date) as month_name
from `tabGL Entry` gl, `tabBudget Detail` bd from `tabGL Entry` gl, `tabBudget Detail` bd
where gl.fiscal_year=%s and company=%s 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) (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): def get_costcenter_account_month_map(filters):
import datetime
costcenter_target_details = get_costcenter_target_details(filters) costcenter_target_details = get_costcenter_target_details(filters)
tdd = get_target_distribution_details(filters) tdd = get_target_distribution_details(filters)
actual_details = get_actual_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 \ month_percentage = ccd.distribution_id and \
tdd.get(ccd.distribution_id, {}).get(month, 0) or 100.0/12 tdd.get(ccd.distribution_id, {}).get(month, 0) or 100.0/12
tav_dict.target = flt(flt(ccd.budget_allocated) * month_percentage /100) tav_dict.target = flt(ccd.budget_allocated) * month_percentage /100
for ad in actual_details: for ad in actual_details.get(ccd.name, {}).get(ccd.account, []):
if ad.month_name == month and ad.account == ccd.account \ if ad.month_name == month:
and ad.cost_center == ccd.name:
tav_dict.actual += ad.debit - ad.credit tav_dict.actual += ad.debit - ad.credit
return cam_map return cam_map

View File

@ -3,10 +3,6 @@
wn.require('app/setup/doctype/contact_control/contact_control.js'); 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.refresh = function(doc,dt,dn) {
cur_frm.cscript.make_dashboard(doc); cur_frm.cscript.make_dashboard(doc);
erpnext.hide_naming_series(); erpnext.hide_naming_series();
@ -32,6 +28,7 @@ cur_frm.cscript.make_dashboard = function(doc) {
cur_frm.dashboard.reset(); cur_frm.dashboard.reset();
if(doc.__islocal) if(doc.__islocal)
return; return;
if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager"))
cur_frm.dashboard.set_headline('<span class="text-muted">Loading...</span>') cur_frm.dashboard.set_headline('<span class="text-muted">Loading...</span>')
cur_frm.dashboard.add_doctype_badge("Supplier Quotation", "supplier"); cur_frm.dashboard.add_doctype_badge("Supplier Quotation", "supplier");
@ -46,12 +43,14 @@ cur_frm.cscript.make_dashboard = function(doc) {
supplier: cur_frm.doc.name supplier: cur_frm.doc.name
}, },
callback: function(r) { callback: function(r) {
if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) {
cur_frm.dashboard.set_headline( cur_frm.dashboard.set_headline(
wn._("Total Billing This Year: ") + "<b>" wn._("Total Billing This Year: ") + "<b>"
+ format_currency(r.message.total_billing, cur_frm.doc.default_currency) + format_currency(r.message.total_billing, cur_frm.doc.default_currency)
+ '</b> / <span class="text-muted">' + wn._("Unpaid") + ": <b>" + '</b> / <span class="text-muted">' + wn._("Unpaid") + ": <b>"
+ format_currency(r.message.total_unpaid, cur_frm.doc.default_currency) + format_currency(r.message.total_unpaid, cur_frm.doc.default_currency)
+ '</b></span>'); + '</b></span>');
}
cur_frm.dashboard.set_badge_count(r.message); cur_frm.dashboard.set_badge_count(r.message);
} }
}) })

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import webnotes import webnotes
from webnotes import _, msgprint from webnotes import _, msgprint
from webnotes.utils import flt, cint, today, cstr from webnotes.utils import flt, cint, today, cstr
from webnotes.model.code import get_obj
from setup.utils import get_company_currency from setup.utils import get_company_currency
from accounts.utils import get_fiscal_year, validate_fiscal_year from accounts.utils import get_fiscal_year, validate_fiscal_year
from utilities.transaction_base import TransactionBase, validate_conversion_rate from utilities.transaction_base import TransactionBase, validate_conversion_rate
@ -423,6 +424,16 @@ class AccountsController(TransactionBase):
return self._abbr 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() @webnotes.whitelist()
def get_tax_rate(account_head): 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")

View File

@ -247,14 +247,14 @@ class SellingController(StockController):
customer_account = webnotes.conn.get_value("Account", {"company": self.doc.company, customer_account = webnotes.conn.get_value("Account", {"company": self.doc.company,
"master_name": self.doc.customer}, "name") "master_name": self.doc.customer}, "name")
if customer_account: if customer_account:
total_outstanding = 0
total_outstanding = webnotes.conn.sql("""select total_outstanding = webnotes.conn.sql("""select
sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) 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) outstanding_including_current = flt(total_outstanding) + flt(grand_total)
webnotes.bean('Account', customer_account).run_method("check_credit_limit", 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): def validate_max_discount(self):
for d in self.doclist.get({"parentfield": self.fname}): for d in self.doclist.get({"parentfield": self.fname}):

View File

@ -11,7 +11,7 @@ from controllers.accounts_controller import AccountsController
from accounts.general_ledger import make_gl_entries, delete_gl_entries from accounts.general_ledger import make_gl_entries, delete_gl_entries
class StockController(AccountsController): class StockController(AccountsController):
def make_gl_entries(self): def make_gl_entries(self, update_gl_entries_after=True):
if self.doc.docstatus == 2: if self.doc.docstatus == 2:
delete_gl_entries(voucher_type=self.doc.doctype, voucher_no=self.doc.name) 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() warehouse_account = self.get_warehouse_account()
if self.doc.docstatus==1: 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) make_gl_entries(gl_entries)
if update_gl_entries_after:
self.update_gl_entries_after(warehouse_account) 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): default_cost_center=None):
from accounts.general_ledger import process_gl_map from accounts.general_ledger import process_gl_map
if not warehouse_account: if not warehouse_account:
@ -99,12 +100,10 @@ class StockController(AccountsController):
gle = self.get_voucherwise_gl_entries(future_stock_vouchers) gle = self.get_voucherwise_gl_entries(future_stock_vouchers)
if not warehouse_account: if not warehouse_account:
warehouse_account = self.get_warehouse_account() warehouse_account = self.get_warehouse_account()
for voucher_type, voucher_no in future_stock_vouchers: for voucher_type, voucher_no in future_stock_vouchers:
existing_gle = gle.get((voucher_type, voucher_no), []) existing_gle = gle.get((voucher_type, voucher_no), [])
voucher_obj = webnotes.get_obj(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: if expected_gle:
matched = True matched = True
if existing_gle: if existing_gle:
@ -121,7 +120,7 @@ class StockController(AccountsController):
if not matched: if not matched:
self.delete_gl_entries(voucher_type, voucher_no) self.delete_gl_entries(voucher_type, voucher_no)
make_gl_entries(expected_gle) voucher_obj.make_gl_entries(update_gl_entries_after=False)
else: else:
self.delete_gl_entries(voucher_type, voucher_no) self.delete_gl_entries(voucher_type, voucher_no)

View File

@ -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()

View File

@ -245,4 +245,5 @@ patch_list = [
"execute:webnotes.reload_doc('core', 'doctype', 'defaultvalue') #2013-11-15", "execute:webnotes.reload_doc('core', 'doctype', 'defaultvalue') #2013-11-15",
"execute:webnotes.reload_doc('core', 'doctype', 'comment') #2013-11-15", "execute:webnotes.reload_doc('core', 'doctype', 'comment') #2013-11-15",
"patches.1311.p02_index_singles", "patches.1311.p02_index_singles",
"patches.1311.p01_make_gl_entries_for_si",
] ]

View File

@ -25,7 +25,11 @@ erpnext.StockGridReport = wn.views.TreeGridReport.extend({
add_qty = 0; add_qty = 0;
} }
} }
if(sl.serial_no) {
var value_diff = this.get_serialized_value_diff(sl);
} else {
var value_diff = (rate * add_qty); var value_diff = (rate * add_qty);
}
if(add_qty) if(add_qty)
wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]); 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() { get_serialized_buying_rates: function() {
var serialized_buying_rates = {}; var serialized_buying_rates = {};
$.each(wn.report_dump.data["Stock Ledger Entry"], function(i, sle) { $.each(wn.report_dump.data["Serial No"], function(i, sn) {
if(sle.qty > 0 && sle.serial_no) { serialized_buying_rates[sn.name.toLowerCase()] = flt(sn.incoming_rate);
$.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);
}
});
}
}); });
return serialized_buying_rates; return serialized_buying_rates;

View File

@ -496,16 +496,17 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, precision("conversion_rate")); 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", var conversion_rate_label = wn.meta.get_label(this.frm.doc.doctype, "conversion_rate",
this.frm.doc.name); this.frm.doc.name);
if(this.frm.doc.conversion_rate == 0) {
wn.throw(wn._(conversion_rate_label) + " " + wn._("cannot be 0"));
}
var company_currency = this.get_company_currency(); var company_currency = this.get_company_currency();
if(!this.frm.doc.conversion_rate) { if(!this.frm.doc.conversion_rate) {
wn.throw(wn._("Please enter valid") + " " + wn._(conversion_rate_label) + wn.throw(repl('%(conversion_rate_label)s' +
" 1 " + this.frm.doc.currency + " = [?] " + company_currency); 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
}));
} }
}, },

View File

@ -41,6 +41,7 @@ cur_frm.cscript.setup_dashboard = function(doc) {
cur_frm.dashboard.reset(doc); cur_frm.dashboard.reset(doc);
if(doc.__islocal) if(doc.__islocal)
return; return;
if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager"))
cur_frm.dashboard.set_headline('<span class="text-muted">'+ wn._('Loading...')+ '</span>') cur_frm.dashboard.set_headline('<span class="text-muted">'+ wn._('Loading...')+ '</span>')
cur_frm.dashboard.add_doctype_badge("Opportunity", "customer"); cur_frm.dashboard.add_doctype_badge("Opportunity", "customer");
@ -56,12 +57,14 @@ cur_frm.cscript.setup_dashboard = function(doc) {
customer: cur_frm.doc.name customer: cur_frm.doc.name
}, },
callback: function(r) { callback: function(r) {
if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) {
cur_frm.dashboard.set_headline( cur_frm.dashboard.set_headline(
wn._("Total Billing This Year: ") + "<b>" wn._("Total Billing This Year: ") + "<b>"
+ format_currency(r.message.total_billing, cur_frm.doc.default_currency) + format_currency(r.message.total_billing, cur_frm.doc.default_currency)
+ '</b> / <span class="text-muted">' + wn._("Unpaid") + ": <b>" + '</b> / <span class="text-muted">' + wn._("Unpaid") + ": <b>"
+ format_currency(r.message.total_unpaid, cur_frm.doc.default_currency) + format_currency(r.message.total_unpaid, cur_frm.doc.default_currency)
+ '</b></span>'); + '</b></span>');
}
cur_frm.dashboard.set_badge_count(r.message); cur_frm.dashboard.set_badge_count(r.message);
} }
}) })

View File

@ -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})

View File

@ -58,7 +58,8 @@ def get_columns(filters):
columns.append(label+":Float:120") 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 #Get sales person & item group details
def get_salesperson_details(filters): def get_salesperson_details(filters):
@ -83,7 +84,7 @@ def get_target_distribution_details(filters):
def get_achieved_details(filters): def get_achieved_details(filters):
start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:] 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 st.sales_person, MONTHNAME(so.transaction_date) as month_name
from `tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st from `tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st
where soi.parent=so.name and so.docstatus=1 and 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'), so.transaction_date<=%s""" % ('%s', '%s'),
(start_date, end_date), as_dict=1) (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): def get_salesperson_item_month_map(filters):
import datetime import datetime
salesperson_details = get_salesperson_details(filters) salesperson_details = get_salesperson_details(filters)
@ -110,17 +118,15 @@ def get_salesperson_item_month_map(filters):
month_percentage = sd.distribution_id and \ month_percentage = sd.distribution_id and \
tdd.get(sd.distribution_id, {}).get(month, 0) or 100.0/12 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"): if (filters["target_on"] == "Quantity"):
tav_dict.target = flt(flt(sd.target_qty) * month_percentage/100, 2) tav_dict.target = flt(sd.target_qty) * month_percentage / 100
if ad.month_name == month and get_item_group(ad.item_code) == sd.item_group \ if ad.month_name == month:
and ad.sales_person == sd.name:
tav_dict.achieved += ad.qty tav_dict.achieved += ad.qty
if (filters["target_on"] == "Amount"): if (filters["target_on"] == "Amount"):
tav_dict.target = flt(flt(sd.target_amount) * month_percentage/100, 2) tav_dict.target = flt(sd.target_amount) * month_percentage / 100
if ad.month_name == month and get_item_group(ad.item_code) == sd.item_group \ if ad.month_name == month:
and ad.sales_person == sd.name:
tav_dict.achieved += ad.amount tav_dict.achieved += ad.amount
return sim_map return sim_map

View File

@ -16,10 +16,7 @@ def execute(filters=None):
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
tim_map = get_territory_item_month_map(filters) tim_map = get_territory_item_month_map(filters)
precision = webnotes.conn.get_value("Global Defaults", None, "float_precision") or 2
data = [] data = []
for territory, territory_items in tim_map.items(): for territory, territory_items in tim_map.items():
for item_group, monthwise_data in territory_items.items(): for item_group, monthwise_data in territory_items.items():
row = [territory, item_group] row = [territory, item_group]
@ -29,7 +26,7 @@ def execute(filters=None):
for month in relevant_months: for month in relevant_months:
month_data = monthwise_data.get(month, {}) month_data = monthwise_data.get(month, {})
for i, fieldname in enumerate(["target", "achieved", "variance"]): 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 period_data[i] += value
totals[i] += value totals[i] += value
period_data[2] = period_data[0] - period_data[1] period_data[2] = period_data[0] - period_data[1]
@ -58,7 +55,8 @@ def get_columns(filters):
label = label % from_date.strftime("%b") label = label % from_date.strftime("%b")
columns.append(label+":Float:120") 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 #Get territory & item group details
def get_territory_details(filters): def get_territory_details(filters):
@ -83,14 +81,22 @@ def get_target_distribution_details(filters):
def get_achieved_details(filters): def get_achieved_details(filters):
start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:] 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 so.territory, MONTHNAME(so.transaction_date) as month_name
from `tabSales Order Item` soi, `tabSales Order` so from `tabSales Order Item` soi, `tabSales Order` so
where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and where soi.parent=so.name and so.docstatus=1 and so.transaction_date>=%s and
so.transaction_date<=%s""" % ('%s', '%s'), so.transaction_date<=%s""" % ('%s', '%s'),
(start_date, end_date), as_dict=1) (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): def get_territory_item_month_map(filters):
import datetime
territory_details = get_territory_details(filters) territory_details = get_territory_details(filters)
tdd = get_target_distribution_details(filters) tdd = get_target_distribution_details(filters)
achieved_details = get_achieved_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 \ month_percentage = td.distribution_id and \
tdd.get(td.distribution_id, {}).get(month, 0) or 100.0/12 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"): if (filters["target_on"] == "Quantity"):
tav_dict.target = flt(flt(td.target_qty) * month_percentage /100) tav_dict.target = flt(td.target_qty) * month_percentage / 100
if ad.month_name == month and get_item_group(ad.item_code) == td.item_group \ if ad.month_name == month:
and ad.territory == td.name:
tav_dict.achieved += ad.qty tav_dict.achieved += ad.qty
if (filters["target_on"] == "Amount"): if (filters["target_on"] == "Amount"):
tav_dict.target = flt(flt(td.target_amount) * month_percentage / 100) tav_dict.target = flt(td.target_amount) * month_percentage / 100
if ad.month_name == month and get_item_group(ad.item_code) == td.item_group \ if ad.month_name == month:
and ad.territory == td.name:
tav_dict.achieved += ad.amount tav_dict.achieved += ad.amount
return tim_map return tim_map

View File

@ -88,6 +88,11 @@ data_map = {
}, },
"force_index": "posting_sort_index" "force_index": "posting_sort_index"
}, },
"Serial No": {
"columns": ["name", "purchase_rate as incoming_rate"],
"conditions": ["docstatus < 2"],
"order_by": "name"
},
"Stock Entry": { "Stock Entry": {
"columns": ["name", "purpose"], "columns": ["name", "purpose"],
"conditions": ["docstatus=1"], "conditions": ["docstatus=1"],

View File

@ -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) 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 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") against_stock_account = self.get_company_default("stock_received_but_not_billed")
gl_entries = super(DocType, self).get_gl_entries_for_stock(warehouse_account, gl_entries = super(DocType, self).get_gl_entries(warehouse_account, against_stock_account)
against_stock_account)
return gl_entries return gl_entries

View File

@ -475,7 +475,7 @@ class DocType(StockController):
self.doc.from_warehouse = "" self.doc.from_warehouse = ""
item = webnotes.conn.sql("""select name, item_name, description, 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)""", where name=(select item from tabBOM where name=%s)""",
self.doc.bom_no, as_dict=1) self.doc.bom_no, as_dict=1)
self.add_to_stock_entry_detail({ self.add_to_stock_entry_detail({
@ -483,7 +483,7 @@ class DocType(StockController):
"qty": self.doc.fg_completed_qty, "qty": self.doc.fg_completed_qty,
"item_name": item[0].item_name, "item_name": item[0].item_name,
"description": item[0]["description"], "description": item[0]["description"],
"stock_uom": item[0]["uom"], "stock_uom": item[0]["stock_uom"],
"from_warehouse": "", "from_warehouse": "",
"expense_account": item[0].purchase_account, "expense_account": item[0].purchase_account,
"cost_center": item[0].cost_center, "cost_center": item[0].cost_center,

View File

@ -275,11 +275,11 @@ class DocType(StockController):
"posting_time": self.doc.posting_time "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: if not self.doc.cost_center:
msgprint(_("Please enter Cost Center"), raise_exception=1) 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) self.doc.expense_account, self.doc.cost_center)

View File

@ -23,5 +23,10 @@ test_records = [
"doctype": "Warehouse User", "doctype": "Warehouse User",
"parentfield": "warehouse_users", "parentfield": "warehouse_users",
"user": "test2@example.com" "user": "test2@example.com"
}] }],
[{
"doctype": "Warehouse",
"warehouse_name": "_Test Warehouse No Account",
"company": "_Test Company",
}],
] ]

View File

@ -22,7 +22,7 @@ erpnext.StockBalance = erpnext.StockAnalytics.extend({
this._super(wrapper, { this._super(wrapper, {
title: wn._("Stock Balance"), title: wn._("Stock Balance"),
doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand", doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand",
"Stock Entry", "Project"], "Stock Entry", "Project", "Serial No"],
}); });
}, },
setup_columns: function() { setup_columns: function() {

View File

@ -426,15 +426,15 @@ def get_address_territory(address_doc):
def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company): def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company):
"""common validation for currency and price list currency""" """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") company_currency = webnotes.conn.get_value("Company", company, "default_currency")
if not conversion_rate: if not conversion_rate:
msgprint(_('Please enter valid ') + conversion_rate_label + (': ') msgprint(_('%(conversion_rate_label)s is mandatory. Maybe Currency Exchange \
+ ("1 %s = [?] %s" % (currency, company_currency)), record is not created for %(from_currency)s to %(to_currency)s') % {
raise_exception=True) "conversion_rate_label": conversion_rate_label,
"from_currency": currency,
"to_currency": company_currency
}, raise_exception=True)
def validate_item_fetch(args, item): def validate_item_fetch(args, item):
from stock.utils import validate_end_of_life from stock.utils import validate_end_of_life