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:
commit
0c26f8116d
@ -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 <b>%s</b> can not be \
|
||||
greater than credit limit (%s). To change your credit limit settings, \
|
||||
please update the <b>%s</b>""" % (fmt_money(tot_outstanding),
|
||||
account, fmt_money(credit_limit), credit_limit_from), raise_exception=1)
|
||||
please update in the <b>%s</b> 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"""
|
||||
|
@ -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":
|
||||
|
@ -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'):
|
||||
|
@ -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:
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
@ -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('<span class="text-muted">Loading...</span>')
|
||||
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.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: ") + "<b>"
|
||||
+ format_currency(r.message.total_billing, cur_frm.doc.default_currency)
|
||||
+ '</b> / <span class="text-muted">' + wn._("Unpaid") + ": <b>"
|
||||
+ format_currency(r.message.total_unpaid, cur_frm.doc.default_currency)
|
||||
+ '</b></span>');
|
||||
if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) {
|
||||
cur_frm.dashboard.set_headline(
|
||||
wn._("Total Billing This Year: ") + "<b>"
|
||||
+ format_currency(r.message.total_billing, cur_frm.doc.default_currency)
|
||||
+ '</b> / <span class="text-muted">' + wn._("Unpaid") + ": <b>"
|
||||
+ format_currency(r.message.total_unpaid, cur_frm.doc.default_currency)
|
||||
+ '</b></span>');
|
||||
}
|
||||
cur_frm.dashboard.set_badge_count(r.message);
|
||||
}
|
||||
})
|
||||
|
@ -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")
|
@ -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}):
|
||||
|
@ -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)
|
||||
|
||||
|
13
patches/1311/p01_make_gl_entries_for_si.py
Normal file
13
patches/1311/p01_make_gl_entries_for_si.py
Normal 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()
|
@ -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",
|
||||
]
|
@ -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;
|
||||
|
@ -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
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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('<span class="text-muted">'+ wn._('Loading...')+ '</span>')
|
||||
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.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: ") + "<b>"
|
||||
+ format_currency(r.message.total_billing, cur_frm.doc.default_currency)
|
||||
+ '</b> / <span class="text-muted">' + wn._("Unpaid") + ": <b>"
|
||||
+ format_currency(r.message.total_unpaid, cur_frm.doc.default_currency)
|
||||
+ '</b></span>');
|
||||
if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) {
|
||||
cur_frm.dashboard.set_headline(
|
||||
wn._("Total Billing This Year: ") + "<b>"
|
||||
+ format_currency(r.message.total_billing, cur_frm.doc.default_currency)
|
||||
+ '</b> / <span class="text-muted">' + wn._("Unpaid") + ": <b>"
|
||||
+ format_currency(r.message.total_unpaid, cur_frm.doc.default_currency)
|
||||
+ '</b></span>');
|
||||
}
|
||||
cur_frm.dashboard.set_badge_count(r.message);
|
||||
}
|
||||
})
|
||||
|
343
selling/doctype/sales_common/sales_common.py
Normal file
343
selling/doctype/sales_common/sales_common.py
Normal 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})
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"],
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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",
|
||||
}],
|
||||
]
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user