[fix] [minor] auto accounting for stock transactions

This commit is contained in:
Nabin Hait
2013-08-29 18:19:37 +05:30
parent d85d63bb81
commit bb77756069
18 changed files with 134 additions and 247 deletions

View File

@@ -186,7 +186,6 @@ class DocType(SellingController):
self.credit_limit()
self.set_buying_amount()
self.make_gl_entries()
# set DN status

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-04-22 13:15:44",
"docstatus": 0,
"modified": "2013-08-07 14:45:30",
"modified": "2013-08-29 16:58:16",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -420,17 +420,6 @@
"print_hide": 1,
"read_only": 1
},
{
"doctype": "DocField",
"fieldname": "buying_amount",
"fieldtype": "Currency",
"hidden": 1,
"label": "Buying Amount",
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1
},
{
"allow_on_submit": 1,
"doctype": "DocField",

View File

@@ -7,10 +7,7 @@ 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
sql = webnotes.conn.sql
from webnotes import msgprint, _
class DocType:
def __init__(self, doc, doclist=[]):
@@ -18,47 +15,66 @@ class DocType:
self.doclist = doclist
self.prwise_cost = {}
def check_mandatory(self):
""" Check mandatory fields """
if not self.doc.from_pr_date or not self.doc.to_pr_date:
msgprint("Please enter From and To PR Date", raise_exception=1)
webnotes.throw(_("Please enter From and To PR Date"))
if not self.doc.currency:
msgprint("Please enter Currency.", raise_exception=1)
webnotes.throw(_("Please enter Currency"))
def update_landed_cost(self):
"""
Add extra cost and recalculate all values in pr,
Recalculate valuation rate in all sle after pr posting date
"""
self.get_selected_pr()
self.validate_selected_pr()
self.add_charges_in_pr()
self.cal_charges_and_item_tax_amt()
self.update_sle()
msgprint("Landed Cost updated successfully")
def get_selected_pr(self):
""" Get selected purchase receipt no """
self.selected_pr = [d.purchase_receipt for d in \
self.doclist.get({"parentfield": "lc_pr_details"}) if d.select_pr]
if not self.selected_pr:
webnotes.throw(_("Please select atleast one PR to proceed."))
def get_purchase_receipts(self):
""" Get purchase receipts for given period """
self.doclist = self.doc.clear_table(self.doclist,'lc_pr_details',1)
self.doclist = self.doc.clear_table(self.doclist,'lc_pr_details')
self.check_mandatory()
pr = sql("select name from `tabPurchase Receipt` where docstatus = 1 and posting_date >= '%s' and posting_date <= '%s' and currency = '%s' order by name " % (self.doc.from_pr_date, self.doc.to_pr_date, self.doc.currency), as_dict = 1)
if len(pr)>200:
msgprint("Please enter date of shorter duration as there are too many purchase receipt, hence it cannot be loaded.", raise_exception=1)
pr = webnotes.conn.sql("""select name from `tabPurchase Receipt` where docstatus = 1
and posting_date>=%s and posting_date<=%s and currency=%s order by name """,
(self.doc.from_pr_date, self.doc.to_pr_date, self.doc.currency), as_dict = 1)
if len(pr) > 200:
webnotes.throw(_("Please enter date of shorter duration as there are too many \
purchase receipt, hence it cannot be loaded."))
for i in pr:
ch = addchild(self.doc, 'lc_pr_details', 'Landed Cost Purchase Receipt',
self.doclist)
ch.purchase_receipt = i and i['name'] or ''
ch.save()
def get_selected_pr(self):
""" Get selected purchase receipt no """
self.selected_pr = [d.purchase_receipt for d in getlist(self.doclist, 'lc_pr_details') if d.select_pr]
if not self.selected_pr:
msgprint("Please select atleast one PR to proceed.", raise_exception=1)
ch.purchase_receipt = i.name
def validate_selected_pr(self):
"""Validate selected PR as submitted"""
invalid_pr = sql("SELECT name FROM `tabPurchase Receipt` WHERE docstatus != 1 and name in (%s)" % ("'" + "', '".join(self.selected_pr) + "'"))
invalid_pr = webnotes.conn.sql("""SELECT name FROM `tabPurchase Receipt`
WHERE docstatus!=1 and name in (%s)""" %
', '.join(['%s']*len(self.selected_pr)), tuple(self.selected_pr))
if invalid_pr:
msgprint("Selected purchase receipts must be submitted. Following PR are not submitted: %s" % invalid_pr, raise_exception=1)
webnotes.throw(_("Selected purchase receipts must be submitted. \
Following PR are not submitted") + ": " + invalid_pr)
def get_total_amt(self):
""" Get sum of net total of all selected PR"""
return sql("SELECT SUM(net_total) FROM `tabPurchase Receipt` WHERE name in (%s)" % ("'" + "', '".join(self.selected_pr) + "'"))[0][0]
return webnotes.conn.sql("""SELECT SUM(net_total) FROM `tabPurchase Receipt`
WHERE name in (%s)""" % ', '.join(['%s']*len(self.selected_pr)),
tuple(self.selected_pr))[0][0]
def add_charges_in_pr(self):
@@ -74,7 +90,9 @@ class DocType:
self.prwise_cost[pr] = self.prwise_cost.get(pr, 0) + amt
cumulative_grand_total += amt
pr_oc_row = sql("select name from `tabPurchase Taxes and Charges` where parent = %s and category = 'Valuation' and add_deduct_tax = 'Add' and charge_type = 'Actual' and account_head = %s",(pr, lc.account_head))
pr_oc_row = webnotes.conn.sql("""select name from `tabPurchase Taxes and Charges`
where parent = %s and category = 'Valuation' and add_deduct_tax = 'Add'
and charge_type = 'Actual' and account_head = %s""",(pr, lc.account_head))
if not pr_oc_row: # add if not exists
ch = addchild(pr_obj.doc, 'purchase_tax_details', 'Purchase Taxes and Charges')
ch.category = 'Valuation'
@@ -89,7 +107,9 @@ class DocType:
ch.idx = 500 # add at the end
ch.save(1)
else: # overwrite if exists
sql("update `tabPurchase Taxes and Charges` set rate = %s, tax_amount = %s where name = %s and parent = %s ", (amt, amt, pr_oc_row[0][0], pr))
webnotes.conn.sql("""update `tabPurchase Taxes and Charges`
set rate = %s, tax_amount = %s where name = %s and parent = %s""",
(amt, amt, pr_oc_row[0][0], pr))
def reset_other_charges(self, pr_obj):
@@ -201,9 +221,9 @@ class DocType:
d.save()
if d.serial_no:
self.update_serial_no(d.serial_no, d.valuation_rate)
sql("update `tabStock Ledger Entry` set incoming_rate = '%s' where voucher_detail_no = '%s'"%(flt(d.valuation_rate), d.name))
webnotes.conn.sql("update `tabStock Ledger Entry` set incoming_rate = '%s' where voucher_detail_no = '%s'"%(flt(d.valuation_rate), d.name))
res = sql("""select item_code, warehouse, posting_date, posting_time
res = webnotes.conn.sql("""select item_code, warehouse, posting_date, posting_time
from `tabStock Ledger Entry` where voucher_detail_no = %s LIMIT 1""",
d.name, as_dict=1)
@@ -211,22 +231,9 @@ class DocType:
if res:
update_entries_after(res[0])
def update_serial_no(self, sr_no, rate):
""" update valuation rate in serial no"""
sr_no = map(lambda x: x.strip(), cstr(sr_no).split('\n'))
webnotes.conn.sql("""update `tabSerial No` set purchase_rate = %s where name in (%s)""" %
('%s', ', '.join(['%s']*len(sr_no))), tuple([rate] + sr_no))
def update_landed_cost(self):
"""
Add extra cost and recalculate all values in pr,
Recalculate valuation rate in all sle after pr posting date
"""
self.get_selected_pr()
self.validate_selected_pr()
self.add_charges_in_pr()
self.cal_charges_and_item_tax_amt()
self.update_sle()
msgprint("Landed Cost updated successfully")
('%s', ', '.join(['%s']*len(sr_no))), tuple([rate] + sr_no))

View File

@@ -39,9 +39,9 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
};
if(cint(wn.defaults.get_default("auto_accounting_for_stock"))) {
this.frm.add_fetch("company", "stock_adjustment_account", "expense_adjustment_account");
this.frm.fields_dict["expense_adjustment_account"].get_query = function() {
this.frm.add_fetch("company", "stock_adjustment_account", "expense_account");
this.frm.fields_dict.mtn_details.grid.get_field('expense_account').get_query =
function() {
return {
filters: {
"company": me.frm.doc.company,
@@ -88,7 +88,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
set_default_account: function() {
var me = this;
if (cint(wn.defaults.get_default("auto_inventory_accounting")) && !this.frm.doc.expense_adjustment_account) {
if(cint(wn.defaults.get_default("auto_accounting_for_stock")) {
var account_for = "stock_adjustment_account";
if (this.frm.doc.purpose == "Sales Return")
account_for = "stock_in_hand_account";
@@ -102,12 +102,22 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
"company": this.frm.doc.company
},
callback: function(r) {
if (!r.exc) me.frm.set_value("expense_adjustment_account", r.message);
if (!r.exc) {
for(d in getchildren('Stock Entry Detail',doc.name,'mtn_details')) {
if(!d.expense_account) d.expense_account = r.message;
}
}
}
});
}
},
entries_add: function(doc, cdt, cdn) {
var row = wn.model.get_doc(cdt, cdn);
this.frm.script_manager.copy_from_first_row("mtn_details", row,
["expense_account", "cost_center"]);
},
clean_up: function() {
// Clear Production Order record from locals, because it is updated via Stock Entry
if(this.frm.doc.production_order &&

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-03-29 18:22:12",
"docstatus": 0,
"modified": "2013-08-28 19:15:55",
"modified": "2013-08-28 19:25:38",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -149,7 +149,7 @@
"doctype": "DocField",
"fieldname": "expense_account",
"fieldtype": "Link",
"label": "Expense/Adjustment Account",
"label": "Difference Account",
"options": "Account",
"print_hide": 1
},

View File

@@ -2,7 +2,7 @@
{
"creation": "2013-03-28 10:35:31",
"docstatus": 0,
"modified": "2013-08-07 18:16:18",
"modified": "2013-08-29 16:46:33",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -102,11 +102,11 @@
"reqd": 1
},
{
"depends_on": "eval:sys_defaults.auto_inventory_accounting",
"depends_on": "eval:sys_defaults.auto_accounting_for_stock",
"doctype": "DocField",
"fieldname": "expense_account",
"fieldtype": "Link",
"label": "Expense Account",
"label": "Difference Account",
"options": "Account"
},
{

View File

@@ -22,7 +22,6 @@ cur_frm.set_query("account", function() {
filters: {
"company": cur_frm.doc.company,
"debit_or_credit": "Debit",
"is_pl_account": "No",
'group_or_ledger': "Ledger"
}
}

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
from webnotes.utils import flt, validate_email_add
from webnotes.utils import cint, flt, validate_email_add
from webnotes.model.code import get_obj
from webnotes import msgprint
@@ -23,6 +23,21 @@ class DocType:
def validate(self):
if self.doc.email_id and not validate_email_add(self.doc.email_id):
msgprint("Please enter valid Email Id", raise_exception=1)
self.account_mandatory()
def account_mandatory(self):
if cint(webnotes.defaults.get_global_default("auto_accounting_for_stock")):
sle_exists = webnotes.conn.get_value("Stock Ledger Entry", {"warehouse": self.doc.name})
if not self.doc.account and (self.doc.__islocal or not sle_exists):
webnotes.throw(_("Asset/Expense Account mandatory"))
if not self.doc.__islocal and sle_exists:
old_account = webnotes.conn.get_value("Warehouse", self.doc.name, "account")
if old_account != self.doc.account:
webnotes.throw(_("Account can not be changed/assigned/removed as \
stock transactions exist for this warehouse"))
def merge_warehouses(self):
webnotes.conn.auto_commit_on_many_writes = 1

View File

@@ -82,7 +82,7 @@ def update_entries_after(args, verbose=1):
valuation_method = get_valuation_method(args["item_code"])
stock_value_difference = 0.0
for sle in entries_to_fix:
if sle.serial_no or not cint(webnotes.conn.get_default("allow_negative_stock")):
# validate negative stock for serialized items, fifo valuation
@@ -90,7 +90,7 @@ def update_entries_after(args, verbose=1):
if not validate_negative_stock(qty_after_transaction, sle):
qty_after_transaction += flt(sle.actual_qty)
continue
if sle.serial_no:
valuation_rate = get_serialized_values(qty_after_transaction, sle, valuation_rate)
elif valuation_method == "Moving Average":
@@ -172,6 +172,7 @@ def get_stock_ledger_entries(args, conditions=None, order="desc", limit=None, fo
return webnotes.conn.sql("""select * from `tabStock Ledger Entry`
where item_code = %%(item_code)s
and warehouse = %%(warehouse)s
and ifnull(is_cancelled, 'No')='No'
%(conditions)s
order by timestamp(posting_date, posting_time) %(order)s, name %(order)s
%(limit)s %(for_update)s""" % {