mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-24 15:39:20 +00:00
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:
@@ -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
|
||||
Reference in New Issue
Block a user