[minor] [cleanup] Landed cost wizard

This commit is contained in:
Nabin Hait 2013-09-02 18:10:36 +05:30
parent da3110fc12
commit 678130f8db
9 changed files with 118 additions and 293 deletions

View File

@ -113,10 +113,6 @@ class StockController(AccountsController):
make_gl_entries(expected_gle)
else:
self.delete_gl_entries(voucher_type, voucher_no)
# else:
# # make adjustment entry on that date
# self.make_adjustment_entry(expected_gle, voucher_obj)
def get_future_stock_vouchers(self):
@ -185,7 +181,7 @@ class StockController(AccountsController):
def check_expense_account(self, item):
if item.fields.has_key("expense_account") and not item.expense_account:
msgprint(_("""Expense account is mandatory for item: """) + item.item_code,
msgprint(_("""Expense/Difference account is mandatory for item: """) + item.item_code,
raise_exception=1)
if item.fields.has_key("expense_account") and not item.cost_center:

View File

@ -210,7 +210,7 @@ Here apart from normal account entries for invoice, "Stock In Hand" and "Cost of
> <tr><th>Item</th><th>Target Warehouse</th><th>Qty</th><th>Rate</th><th>Amount</th></tr>
> </thead>
> <tbody>
> <tr><td>RM0001</td><td>Stores</td><td>50</td><td>200</td><td>10000</td></tr>
> <tr><td>RM0001</td><td>Stores</td><td>50</td><td>220</td><td>11000</td></tr>
> </tbody>
></table>
@ -221,7 +221,7 @@ Here apart from normal account entries for invoice, "Stock In Hand" and "Cost of
**General Ledger**
![si_stock_ledger](img/accounting-for-stock-10.png)
--
### **Stock Entry (Material Issue)**
@ -232,7 +232,7 @@ Here apart from normal account entries for invoice, "Stock In Hand" and "Cost of
> <tr><th>Item</th><th>Source Warehouse</th><th>Qty</th><th>Rate</th><th>Amount</th></tr>
> </thead>
> <tbody>
> <tr><td>RM0001</td><td>Stores</td><td>10</td><td>200</td><td>2000</td></tr>
> <tr><td>RM0001</td><td>Stores</td><td>10</td><td>220</td><td>2200</td></tr>
> </tbody>
></table>
@ -256,7 +256,7 @@ Here apart from normal account entries for invoice, "Stock In Hand" and "Cost of
> </thead>
> <tbody>
> <tr><td>RM0001</td><td>Stores</td><td>Work In Progress</td>
> <td>10</td><td>200</td><td>2000</td></tr>
> <td>10</td><td>220</td><td>2200</td></tr>
> </tbody>
></table>

View File

@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:28:02",
"docstatus": 0,
"modified": "2013-07-10 14:54:10",
"modified": "2013-09-02 17:36:19",
"modified_by": "Administrator",
"owner": "wasim@webnotestech.com"
},
@ -14,7 +14,6 @@
},
{
"doctype": "DocField",
"in_list_view": 1,
"name": "__common__",
"parent": "Landed Cost Item",
"parentfield": "fields",
@ -30,16 +29,25 @@
"doctype": "DocField",
"fieldname": "account_head",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Account Head",
"oldfieldname": "account_head",
"oldfieldtype": "Link",
"options": "Account",
"search_index": 1
},
{
"doctype": "DocField",
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"doctype": "DocField",
"fieldname": "description",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Description",
"oldfieldname": "description",
"oldfieldtype": "Data",
@ -50,6 +58,7 @@
"doctype": "DocField",
"fieldname": "amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Amount",
"oldfieldname": "amount",
"oldfieldtype": "Currency",

View File

@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:28:02",
"docstatus": 0,
"modified": "2013-07-10 14:54:10",
"modified": "2013-09-02 13:44:28",
"modified_by": "Administrator",
"owner": "wasim@webnotestech.com"
},
@ -14,36 +14,26 @@
},
{
"doctype": "DocField",
"fieldname": "purchase_receipt",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Purchase Receipt",
"name": "__common__",
"oldfieldname": "purchase_receipt_no",
"oldfieldtype": "Link",
"options": "Purchase Receipt",
"parent": "Landed Cost Purchase Receipt",
"parentfield": "fields",
"parenttype": "DocType",
"permlevel": 0
"permlevel": 0,
"print_width": "220px",
"width": "220px"
},
{
"doctype": "DocType",
"name": "Landed Cost Purchase Receipt"
},
{
"doctype": "DocField",
"fieldname": "purchase_receipt",
"fieldtype": "Link",
"label": "Purchase Receipt",
"oldfieldname": "purchase_receipt_no",
"oldfieldtype": "Link",
"options": "Purchase Receipt",
"print_width": "220px",
"width": "220px"
},
{
"doctype": "DocField",
"fieldname": "select_pr",
"fieldtype": "Check",
"label": "Select PR",
"oldfieldname": "include_in_landed_cost",
"oldfieldtype": "Check",
"print_width": "120px",
"width": "120px"
"doctype": "DocField"
}
]

View File

@ -1,13 +1,15 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.onload = function(doc, cdt, cdn) {
if(!doc.currency){doc.currency = sys_defaults.currency;}
cur_frm.fields_dict['lc_pr_details'].grid.get_field("purchase_receipt").get_query = function(doc, cdt, cdn) {
return {
filters:[['Purchase Receipt', 'docstatus', '=', '1']]
}
}
cur_frm.fields_dict['landed_cost_details'].grid.get_field("account_head").get_query = function(doc,cdt,cdn) {
return{
cur_frm.fields_dict['landed_cost_details'].grid.get_field("account_head").get_query = function(doc, cdt, cdn) {
return{
filters:[
['Account', 'group_or_ledger', '=', 'Ledger'],
['Account', 'account_type', 'in', 'Tax, Chargeable'],

View File

@ -13,81 +13,36 @@ class DocType:
def __init__(self, doc, doclist=[]):
self.doc = doc
self.doclist = doclist
self.prwise_cost = {}
def check_mandatory(self):
if not self.doc.from_pr_date or not self.doc.to_pr_date:
webnotes.throw(_("Please enter From and To PR Date"))
if not self.doc.currency:
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()
"""
purchase_receipts = [row.purchase_receipt for row in
self.doclist.get({"parentfield": "lc_pr_details"})]
self.validate_purchase_receipts(purchase_receipts)
self.cancel_pr(purchase_receipts)
self.add_charges_in_pr(purchase_receipts)
self.submit_pr(purchase_receipts)
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')
self.check_mandatory()
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.name
def validate_selected_pr(self):
"""Validate selected PR as submitted"""
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:
webnotes.throw(_("Selected purchase receipts must be submitted. \
Following PR are not submitted") + ": " + invalid_pr)
def validate_purchase_receipts(self, purchase_receipts):
for pr in purchase_receipts:
if webnotes.conn.get_value("Purchase Receipt", pr, "docstatus") != 1:
webnotes.throw(_("Purchase Receipt") + ": " + pr + _(" is not submitted document"))
def get_total_amt(self):
""" Get sum of net total of all selected PR"""
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):
def add_charges_in_pr(self, purchase_receipts):
""" Add additional charges in selected pr proportionately"""
total_amt = self.get_total_amt()
total_amt = self.get_total_pr_amt(purchase_receipts)
for pr in self.selected_pr:
for pr in purchase_receipts:
pr_obj = get_obj('Purchase Receipt', pr, with_children = 1)
cumulative_grand_total = flt(pr_obj.doc.grand_total)
idx = max([d.idx for d in pr_obj.doclist.get({"parentfield": "purchase_tax_details"})])
for lc in getlist(self.doclist, 'landed_cost_details'):
for lc in self.doclist.get({"parentfield": "landed_cost_details"}):
amt = flt(lc.amount) * flt(pr_obj.doc.net_total)/ flt(total_amt)
self.prwise_cost[pr] = self.prwise_cost.get(pr, 0) + amt
cumulative_grand_total += amt
pr_oc_row = webnotes.conn.sql("""select name from `tabPurchase Taxes and Charges`
where parent = %s and category = 'Valuation' and add_deduct_tax = 'Add'
@ -99,140 +54,43 @@ class DocType:
ch.charge_type = 'Actual'
ch.description = lc.description
ch.account_head = lc.account_head
ch.cost_center = lc.cost_center
ch.rate = amt
ch.tax_amount = amt
ch.total = cumulative_grand_total
ch.docstatus = 1
ch.idx = 500 # add at the end
ch.idx = idx
ch.save(1)
idx += 1
else: # overwrite if exists
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):
""" Reset all calculated values to zero"""
for t in getlist(pr_obj.doclist, 'purchase_tax_details'):
t.total_tax_amount = 0;
t.total_amount = 0;
t.tax_amount = 0;
t.total = 0;
t.save()
def cal_charges_and_item_tax_amt(self):
""" Re-calculates other charges values and itemwise tax amount for getting valuation rate"""
import json
for pr in self.selected_pr:
obj = get_obj('Purchase Receipt', pr, with_children = 1)
total = 0
self.reset_other_charges(obj)
for prd in getlist(obj.doclist, 'purchase_receipt_details'):
prev_total, item_tax = flt(prd.amount), 0
total += flt(prd.qty) * flt(prd.purchase_rate)
try:
item_tax_rate = prd.item_tax_rate and json.loads(prd.item_tax_rate) or {}
except ValueError:
item_tax_rate = prd.item_tax_rate and eval(prd.item_tax_rate) or {}
ocd = getlist(obj.doclist, 'purchase_tax_details')
# calculate tax for other charges
for oc in range(len(ocd)):
# Get rate : consider if diff for this item
if item_tax_rate.get(ocd[oc].account_head) and ocd[oc].charge_type != 'Actual':
rate = item_tax_rate[ocd[oc].account_head]
else:
rate = flt(ocd[oc].rate)
tax_amount = self.cal_tax(ocd, prd, rate, obj.doc.net_total, oc)
total, prev_total, item_tax = self.add_deduct_taxes(ocd, oc, tax_amount, total, prev_total, item_tax)
prd.item_tax_amount = flt(item_tax)
prd.save()
obj.doc.save()
pr_obj.calculate_taxes_and_totals()
for d in pr_obj.doclist:
d.save()
def cal_tax(self, ocd, prd, rate, net_total, oc):
""" Calculates tax amount for one item"""
tax_amount = 0
if ocd[oc].charge_type == 'Actual':
tax_amount = flt(rate) * flt(prd.amount) / flt(net_total)
elif ocd[oc].charge_type == 'On Net Total':
tax_amount = flt(rate) * flt(prd.amount) / 100
elif ocd[oc].charge_type == 'On Previous Row Amount':
row_no = cstr(ocd[oc].row_id)
row = row_no.split("+")
for r in range(0, len(row)):
id = cint(row[r])
tax_amount += flt((flt(rate) * flt(ocd[id-1].total_amount) / 100))
row_id = row_no.find("/")
if row_id != -1:
rate = ''
row = (row_no).split("/")
id1 = cint(row[0])
id2 = cint(row[1])
tax_amount = flt(flt(ocd[id1-1].total_amount) / flt(ocd[id2-1].total_amount))
elif ocd[oc].charge_type == 'On Previous Row Total':
row = cint(ocd[oc].row_id)
if ocd[row-1].add_deduct_tax == 'Add':
tax_amount = flt(rate) * (flt(ocd[row-1].total_tax_amount)+flt(ocd[row-1].total_amount)) / 100
elif ocd[row-1].add_deduct_tax == 'Deduct':
tax_amount = flt(rate) * (flt(ocd[row-1].total_tax_amount)-flt(ocd[row-1].total_amount)) / 100
return tax_amount
def add_deduct_taxes(self, ocd, oc, tax_amount, total, prev_total, item_tax):
"""Calculates other charges values"""
add_ded = ocd[oc].add_deduct_tax == 'Add' and 1 or ocd[oc].add_or_deduct == 'Deduct' and -1
ocd[oc].total_amount = flt(tax_amount)
ocd[oc].total_tax_amount = flt(prev_total)
ocd[oc].tax_amount += flt(tax_amount)
if ocd[oc].category != "Valuation":
prev_total += add_ded * flt(ocd[oc].total_amount)
total += add_ded * flt(ocd[oc].tax_amount)
ocd[oc].total = total
else:
prev_total = prev_total
ocd[oc].total = flt(total)
ocd[oc].save()
if ocd[oc].category != "Total":
item_tax += add_ded * ocd[oc].total_amount
return total, prev_total, item_tax
def update_sle(self):
""" Recalculate valuation rate in all sle after pr posting date"""
from stock.stock_ledger import update_entries_after
for pr in self.selected_pr:
pr_obj = get_obj('Purchase Receipt', pr, with_children = 1)
def get_total_pr_amt(self, purchase_receipts):
return webnotes.conn.sql("""SELECT SUM(net_total) FROM `tabPurchase Receipt`
WHERE name in (%s)""" % ', '.join(['%s']*len(purchase_receipts)),
tuple(purchase_receipts))[0][0]
for d in getlist(pr_obj.doclist, 'purchase_receipt_details'):
if flt(d.qty):
d.valuation_rate = (flt(d.purchase_rate) + (flt(d.rm_supp_cost)/flt(d.qty)) + (flt(d.item_tax_amount)/flt(d.qty))) / flt(d.conversion_factor)
d.save()
if d.serial_no:
self.update_serial_no(d.serial_no, d.valuation_rate)
webnotes.conn.sql("update `tabStock Ledger Entry` set incoming_rate = '%s' where voucher_detail_no = '%s'"%(flt(d.valuation_rate), d.name))
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)
# update valuation rate after pr posting date
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 cancel_pr(self, purchase_receipts):
for pr in purchase_receipts:
pr_bean = webnotes.bean("Purchase Receipt", pr)
pr_bean.run_method("update_ordered_qty", is_cancelled="Yes")
pr_bean.run_method("update_serial_nos", cancel=True)
webnotes.conn.sql("""delete from `tabStock Ledger Entry`
where voucher_type='Purchase Receipt' and voucher_no=%s""", pr)
webnotes.conn.sql("""delete from `tabGL Entry` where voucher_type='Purchase Receipt'
and voucher_no=%s""", pr)
def submit_pr(self, purchase_receipts):
for pr in purchase_receipts:
pr_bean = webnotes.bean("Purchase Receipt", pr)
pr_bean.run_method("update_ordered_qty")
pr_bean.run_method("update_stock")
pr_bean.run_method("update_serial_nos")
pr_bean.run_method("make_gl_entries")

View File

@ -2,7 +2,7 @@
{
"creation": "2013-01-22 16:50:39",
"docstatus": 0,
"modified": "2013-07-22 15:31:20",
"modified": "2013-09-02 13:45:54",
"modified_by": "Administrator",
"owner": "wasim@webnotestech.com"
},
@ -38,48 +38,13 @@
"doctype": "DocType",
"name": "Landed Cost Wizard"
},
{
"doctype": "DocField",
"fieldname": "process",
"fieldtype": "HTML",
"label": "Process",
"options": "<div class=\"field_description\"><b>Process:</b><br>1. Fetch and select Purchase Receipt<br>2. Enter extra costs<br>3. Click on Update Landed Cost button<br> 4. Cost will be added into other charges table of selected PR proportionately based on net total<br>5. Item Valuation Rate will be recalculated</div>"
},
{
"doctype": "DocField",
"fieldname": "section_break0",
"fieldtype": "Section Break",
"label": "Select Purchase Receipts",
"options": "Simple"
},
{
"doctype": "DocField",
"fieldname": "from_pr_date",
"fieldtype": "Date",
"label": "From PR Date",
"reqd": 1
},
{
"doctype": "DocField",
"fieldname": "to_pr_date",
"fieldtype": "Date",
"label": "To PR Date",
"reqd": 1
},
{
"doctype": "DocField",
"fieldname": "currency",
"fieldtype": "Link",
"label": "Currency",
"options": "Currency",
"reqd": 1
},
{
"doctype": "DocField",
"fieldname": "get_purchase_receipt",
"fieldtype": "Button",
"label": "Get Purchase Receipt",
"options": "get_purchase_receipts"
},
{
"doctype": "DocField",
"fieldname": "lc_pr_details",
@ -91,7 +56,7 @@
"doctype": "DocField",
"fieldname": "section_break1",
"fieldtype": "Section Break",
"options": "Simple"
"label": "Add Taxes and Charges"
},
{
"doctype": "DocField",
@ -102,9 +67,9 @@
},
{
"doctype": "DocField",
"fieldname": "update_pr",
"fieldname": "update_landed_cost",
"fieldtype": "Button",
"label": "Update PR",
"label": "Update Landed Cost",
"options": "update_landed_cost"
},
{

View File

@ -151,7 +151,6 @@ class DocType(BuyingController):
for d in getlist(self.doclist, 'purchase_receipt_details'):
if d.item_code in stock_items and d.warehouse:
pr_qty = flt(d.qty) * flt(d.conversion_factor)
self.update_ordered_qty(pr_qty, d)
if pr_qty:
sl_entries.append(self.get_sl_entries(d, {
@ -171,28 +170,33 @@ class DocType(BuyingController):
self.bk_flush_supp_wh(sl_entries)
self.make_sl_entries(sl_entries)
def update_ordered_qty(self, pr_qty, d):
def update_ordered_qty(self, is_cancelled="No"):
pc_obj = get_obj('Purchase Common')
if cstr(d.prevdoc_doctype) == 'Purchase Order':
# get qty and pending_qty of prevdoc
curr_ref_qty = pc_obj.get_qty( d.doctype, 'prevdoc_detail_docname',
d.prevdoc_detail_docname, 'Purchase Order Item',
'Purchase Order - Purchase Receipt', self.doc.name)
max_qty, qty, curr_qty = flt(curr_ref_qty.split('~~~')[1]), \
flt(curr_ref_qty.split('~~~')[0]), 0
stock_items = self.get_stock_items()
for d in getlist(self.doclist, 'purchase_receipt_details'):
if d.item_code in stock_items and d.warehouse \
and cstr(d.prevdoc_doctype) == 'Purchase Order':
pr_qty = flt(d.qty) * flt(d.conversion_factor)
if flt(qty) + flt(pr_qty) > flt(max_qty):
curr_qty = (flt(max_qty) - flt(qty)) * flt(d.conversion_factor)
else:
curr_qty = flt(pr_qty)
args = {
"item_code": d.item_code,
"warehouse": d.warehouse,
"posting_date": self.doc.posting_date,
"ordered_qty": self.doc.docstatus==1 and -1*flt(curr_qty) or flt(curr_qty)
}
update_bin(args)
# get qty and pending_qty of prevdoc
curr_ref_qty = pc_obj.get_qty(d.doctype, 'prevdoc_detail_docname',
d.prevdoc_detail_docname, 'Purchase Order Item',
'Purchase Order - Purchase Receipt', self.doc.name)
max_qty, qty, curr_qty = flt(curr_ref_qty.split('~~~')[1]), \
flt(curr_ref_qty.split('~~~')[0]), 0
if flt(qty) + flt(pr_qty) > flt(max_qty):
curr_qty = (flt(max_qty) - flt(qty)) * flt(d.conversion_factor)
else:
curr_qty = flt(pr_qty)
args = {
"item_code": d.item_code,
"warehouse": d.warehouse,
"posting_date": self.doc.posting_date,
"ordered_qty": (is_cancelled=="Yes" and -1 or 1)*flt(curr_qty)
}
update_bin(args)
def bk_flush_supp_wh(self, sl_entries):
for d in getlist(self.doclist, 'pr_raw_material_details'):
@ -234,12 +238,12 @@ class DocType(BuyingController):
self.update_prevdoc_status()
# Update Stock
self.update_ordered_qty()
self.update_stock()
self.update_serial_nos()
# Update last purchase rate
purchase_controller.update_last_purchase_rate(self, 1)
self.make_gl_entries()
@ -270,7 +274,7 @@ class DocType(BuyingController):
pc_obj = get_obj('Purchase Common')
self.check_for_stopped_status(pc_obj)
# 1.Check if Purchase Invoice has been submitted against current Purchase Order
# Check if Purchase Invoice has been submitted against current Purchase Order
# pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Invoice', docname = self.doc.name, detail_doctype = 'Purchase Invoice Item')
submitted = webnotes.conn.sql("select t1.name from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2 where t1.name = t2.parent and t2.purchase_receipt = '%s' and t1.docstatus = 1" % self.doc.name)
@ -278,10 +282,11 @@ class DocType(BuyingController):
msgprint("Purchase Invoice : " + cstr(submitted[0][0]) + " has already been submitted !")
raise Exception
# 2.Set Status as Cancelled
webnotes.conn.set(self.doc,'status','Cancelled')
# 3. Cancel Serial No
self.update_ordered_qty(is_cancelled="Yes")
self.update_stock()
self.update_serial_nos(cancel=True)

View File

@ -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_accounting_for_stock")) {
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";