Merge branch 'nabinhait-landed_cost' into develop
This commit is contained in:
commit
c7105a1a79
@ -14,6 +14,7 @@ def _make_test_records(verbose):
|
||||
["_Test Account Stock Expenses", "Direct Expenses", "Group", None],
|
||||
["_Test Account Shipping Charges", "_Test Account Stock Expenses", "Ledger", "Chargeable"],
|
||||
["_Test Account Customs Duty", "_Test Account Stock Expenses", "Ledger", "Tax"],
|
||||
["_Test Account Insurance Charges", "_Test Account Stock Expenses", "Ledger", "Chargeable"],
|
||||
|
||||
|
||||
["_Test Account Tax Assets", "Current Assets", "Group", None],
|
||||
|
@ -273,7 +273,10 @@ class PurchaseInvoice(BuyingController):
|
||||
def make_gl_entries(self):
|
||||
auto_accounting_for_stock = \
|
||||
cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
|
||||
|
||||
|
||||
stock_received_but_not_billed = self.get_company_default("stock_received_but_not_billed")
|
||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||
|
||||
gl_entries = []
|
||||
|
||||
# parent's gl entry
|
||||
@ -313,30 +316,10 @@ class PurchaseInvoice(BuyingController):
|
||||
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount)
|
||||
|
||||
# item gl entries
|
||||
stock_item_and_auto_accounting_for_stock = False
|
||||
negative_expense_to_be_booked = 0.0
|
||||
stock_items = self.get_stock_items()
|
||||
for item in self.get("entries"):
|
||||
if auto_accounting_for_stock and item.item_code in stock_items:
|
||||
if flt(item.valuation_rate):
|
||||
# if auto inventory accounting enabled and stock item,
|
||||
# then do stock related gl entries
|
||||
# expense will be booked in sales invoice
|
||||
stock_item_and_auto_accounting_for_stock = True
|
||||
|
||||
valuation_amt = flt(item.base_amount + item.item_tax_amount,
|
||||
self.precision("base_amount", item))
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
"against": self.credit_to,
|
||||
"debit": valuation_amt,
|
||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||
})
|
||||
)
|
||||
|
||||
elif flt(item.base_amount):
|
||||
# if not a stock item or auto inventory accounting disabled, book the expense
|
||||
if flt(item.base_amount):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
@ -346,23 +329,53 @@ class PurchaseInvoice(BuyingController):
|
||||
"cost_center": item.cost_center
|
||||
})
|
||||
)
|
||||
|
||||
if auto_accounting_for_stock and item.item_code in stock_items and item.item_tax_amount:
|
||||
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
|
||||
negative_expense_booked_in_pi = None
|
||||
if item.purchase_receipt:
|
||||
negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabGL Entry`
|
||||
where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""",
|
||||
(item.purchase_receipt, expenses_included_in_valuation))
|
||||
|
||||
if not negative_expense_booked_in_pi:
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": stock_received_but_not_billed,
|
||||
"against": self.credit_to,
|
||||
"debit": flt(item.item_tax_amount),
|
||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||
})
|
||||
)
|
||||
|
||||
negative_expense_to_be_booked += flt(item.item_tax_amount)
|
||||
|
||||
if stock_item_and_auto_accounting_for_stock and valuation_tax:
|
||||
|
||||
if negative_expense_to_be_booked and valuation_tax:
|
||||
# credit valuation tax amount in "Expenses Included In Valuation"
|
||||
# this will balance out valuation amount included in cost of goods sold
|
||||
expenses_included_in_valuation = \
|
||||
self.get_company_default("expenses_included_in_valuation")
|
||||
|
||||
|
||||
total_valuation_amount = sum(valuation_tax.values())
|
||||
amount_including_divisional_loss = negative_expense_to_be_booked
|
||||
i = 1
|
||||
for cost_center, amount in valuation_tax.items():
|
||||
if i == len(valuation_tax):
|
||||
applicable_amount = amount_including_divisional_loss
|
||||
else:
|
||||
applicable_amount = negative_expense_to_be_booked * (amount / total_valuation_amount)
|
||||
amount_including_divisional_loss -= applicable_amount
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": expenses_included_in_valuation,
|
||||
"cost_center": cost_center,
|
||||
"against": self.credit_to,
|
||||
"credit": amount,
|
||||
"credit": applicable_amount,
|
||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||
})
|
||||
)
|
||||
|
||||
i += 1
|
||||
|
||||
# writeoff account includes petty difference in the invoice amount
|
||||
# and the amount that is paid
|
||||
@ -387,7 +400,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.make_cancel_gl_entries()
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
def on_update(self):
|
||||
pass
|
||||
|
@ -9,7 +9,8 @@ import frappe.model
|
||||
import json
|
||||
from frappe.utils import cint
|
||||
import frappe.defaults
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
||||
test_records as pr_test_records
|
||||
|
||||
test_dependencies = ["Item", "Cost Center"]
|
||||
test_ignore = ["Serial No"]
|
||||
@ -57,9 +58,41 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
expected_values = sorted([
|
||||
["_Test Supplier - _TC", 0, 720],
|
||||
["Stock Received But Not Billed - _TC", 750.0, 0],
|
||||
["Expenses Included In Valuation - _TC", 0.0, 250.0],
|
||||
["_Test Account Shipping Charges - _TC", 100.0, 0],
|
||||
["_Test Account VAT - _TC", 120.0, 0],
|
||||
])
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEquals(expected_values[i][0], gle.account)
|
||||
self.assertEquals(expected_values[i][1], gle.debit)
|
||||
self.assertEquals(expected_values[i][2], gle.credit)
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
def test_gl_entries_with_auto_accounting_for_stock_against_pr(self):
|
||||
set_perpetual_inventory(1)
|
||||
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
|
||||
|
||||
pr = frappe.copy_doc(pr_test_records[0])
|
||||
pr.submit()
|
||||
|
||||
pi = frappe.copy_doc(test_records[1])
|
||||
for d in pi.get("entries"):
|
||||
d.purchase_receipt = pr.name
|
||||
pi.insert()
|
||||
pi.submit()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||
order by account asc""", pi.name, as_dict=1)
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
expected_values = sorted([
|
||||
["_Test Supplier - _TC", 0, 720],
|
||||
["Stock Received But Not Billed - _TC", 500.0, 0],
|
||||
["_Test Account Shipping Charges - _TC", 100.0, 0],
|
||||
["_Test Account VAT - _TC", 120.0, 0],
|
||||
["Expenses Included In Valuation - _TC", 0, 250.0],
|
||||
])
|
||||
|
||||
for i, gle in enumerate(gl_entries):
|
||||
|
@ -138,6 +138,7 @@
|
||||
}
|
||||
],
|
||||
"posting_date": "2013-02-03",
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_name": "_Test Supplier"
|
||||
},
|
||||
{
|
||||
@ -201,6 +202,7 @@
|
||||
}
|
||||
],
|
||||
"posting_date": "2013-02-03",
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_name": "_Test Supplier"
|
||||
}
|
||||
]
|
||||
|
@ -121,7 +121,7 @@ class SalesInvoice(SellingController):
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
||||
|
||||
self.make_cancel_gl_entries()
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
def update_status_updater_args(self):
|
||||
if cint(self.update_stock):
|
||||
|
@ -502,7 +502,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"warehouse": "_Test Warehouse - _TC"
|
||||
})
|
||||
|
||||
pos_setting.insert()
|
||||
if not frappe.db.exists("POS Setting", "_Test POS Setting"):
|
||||
pos_setting.insert()
|
||||
|
||||
def test_si_gl_entry_with_aii_and_update_stock_with_warehouse_but_no_account(self):
|
||||
self.clear_stock_account_balance()
|
||||
|
@ -193,9 +193,13 @@ class BuyingController(StockController):
|
||||
"UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom},
|
||||
"conversion_factor")) or 1
|
||||
qty_in_stock_uom = flt(item.qty * item.conversion_factor)
|
||||
rm_supp_cost = item.rm_supp_cost if self.doctype=="Purchase Receipt" else 0.0
|
||||
item.valuation_rate = ((item.base_amount + item.item_tax_amount + rm_supp_cost)
|
||||
/ qty_in_stock_uom)
|
||||
rm_supp_cost = flt(item.rm_supp_cost) if self.doctype=="Purchase Receipt" else 0.0
|
||||
|
||||
landed_cost_voucher_amount = flt(item.landed_cost_voucher_amount) \
|
||||
if self.doctype == "Purchase Receipt" else 0.0
|
||||
|
||||
item.valuation_rate = ((item.base_amount + item.item_tax_amount + rm_supp_cost
|
||||
+ landed_cost_voucher_amount) / qty_in_stock_uom)
|
||||
else:
|
||||
item.valuation_rate = 0.0
|
||||
|
||||
|
@ -274,7 +274,7 @@ class StockController(AccountsController):
|
||||
from erpnext.stock.stock_ledger import make_sl_entries
|
||||
make_sl_entries(sl_entries, is_amended)
|
||||
|
||||
def make_cancel_gl_entries(self):
|
||||
def make_gl_entries_on_cancel(self):
|
||||
if frappe.db.sql("""select name from `tabGL Entry` where voucher_type=%s
|
||||
and voucher_no=%s""", (self.doctype, self.name)):
|
||||
self.make_gl_entries()
|
||||
|
@ -76,3 +76,4 @@ erpnext.patches.v4_2.toggle_rounded_total #2014-07-30
|
||||
erpnext.patches.v4_2.fix_account_master_type
|
||||
erpnext.patches.v4_2.update_project_milestones
|
||||
erpnext.patches.v4_2.add_currency_turkish_lira #2014-08-08
|
||||
execute:frappe.delete_doc("DocType", "Landed Cost Wizard")
|
||||
|
@ -196,7 +196,7 @@ class DeliveryNote(SellingController):
|
||||
frappe.db.set(self, 'status', 'Cancelled')
|
||||
self.cancel_packing_slips()
|
||||
|
||||
self.make_cancel_gl_entries()
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
def validate_packed_qty(self):
|
||||
"""
|
||||
|
@ -1,27 +1,18 @@
|
||||
{
|
||||
"creation": "2013-02-22 01:28:02.000000",
|
||||
"creation": "2013-02-22 01:28:02",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "account_head",
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Account Head",
|
||||
"oldfieldname": "account_head",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"label": "Item Code",
|
||||
"options": "Item",
|
||||
"permlevel": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
@ -32,8 +23,42 @@
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_width": "300px",
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"width": "300px"
|
||||
"width": "120px"
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_receipt",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"label": "Purchase Receipt",
|
||||
"no_copy": 1,
|
||||
"options": "Purchase Receipt",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break2",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Qty",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 0,
|
||||
"label": "Rate",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
@ -42,16 +67,37 @@
|
||||
"label": "Amount",
|
||||
"oldfieldname": "amount",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "applicable_charges",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Applicable Charges",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_receipt_item",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Purchase Receipt Item",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2013-12-20 19:23:18.000000",
|
||||
"modified": "2014-08-08 13:11:29.438664",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Item",
|
||||
"owner": "wasim@webnotestech.com"
|
||||
"owner": "wasim@webnotestech.com",
|
||||
"permissions": []
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"creation": "2013-02-22 01:28:02.000000",
|
||||
"creation": "2013-02-22 01:28:02",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
@ -13,14 +13,48 @@
|
||||
"options": "Purchase Receipt",
|
||||
"permlevel": 0,
|
||||
"print_width": "220px",
|
||||
"read_only": 0,
|
||||
"reqd": 1,
|
||||
"width": "220px"
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Supplier",
|
||||
"options": "Supplier",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Posting Date",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "grand_total",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Grand Total",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2013-12-20 19:23:18.000000",
|
||||
"modified": "2014-07-11 12:12:08.572263",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Purchase Receipt",
|
||||
"owner": "wasim@webnotestech.com"
|
||||
"owner": "wasim@webnotestech.com",
|
||||
"permissions": []
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
{
|
||||
"creation": "2014-07-11 11:51:00.453717",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break3",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Account",
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2014-08-08 13:12:02.594698",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Taxes and Charges",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class LandedCostTaxesandCharges(Document):
|
||||
pass
|
110
erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
Normal file
110
erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
frappe.provide("erpnext.stock");
|
||||
frappe.require("assets/erpnext/js/controllers/stock_controller.js");
|
||||
|
||||
erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||
setup: function() {
|
||||
var me = this;
|
||||
this.frm.fields_dict.landed_cost_purchase_receipts.grid.get_field('purchase_receipt').get_query =
|
||||
function() {
|
||||
if(!me.frm.doc.company) msgprint(__("Please enter company first"));
|
||||
return {
|
||||
filters:[
|
||||
['Purchase Receipt', 'docstatus', '=', '1'],
|
||||
['Purchase Receipt', 'company', '=', me.frm.doc.company],
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
this.frm.fields_dict.landed_cost_taxes_and_charges.grid.get_field('account').get_query = function() {
|
||||
if(!me.frm.doc.company) msgprint(__("Please enter company first"));
|
||||
return {
|
||||
filters:[
|
||||
['Account', 'group_or_ledger', '=', 'Ledger'],
|
||||
['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Expense Account']],
|
||||
['Account', 'company', '=', me.frm.doc.company]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
this.frm.add_fetch("purchase_receipt", "supplier", "supplier");
|
||||
this.frm.add_fetch("purchase_receipt", "posting_date", "posting_date");
|
||||
this.frm.add_fetch("purchase_receipt", "grand_total", "grand_total");
|
||||
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
var help_content = ['<table class="table table-bordered" style="background-color: #f9f9f9;">',
|
||||
'<tr><td>',
|
||||
'<h4><i class="icon-hand-right"></i> ',
|
||||
__('Notes'),
|
||||
':</h4>',
|
||||
'<ul>',
|
||||
'<li>',
|
||||
__("Charges will be distributed proportionately based on item amount"),
|
||||
'</li>',
|
||||
'<li>',
|
||||
__("Remove item if charges is not applicable to that item"),
|
||||
'</li>',
|
||||
'<li>',
|
||||
__("Charges are updated in Purchase Receipt against each item"),
|
||||
'</li>',
|
||||
'<li>',
|
||||
__("Item valuation rate is recalculated considering landed cost voucher amount"),
|
||||
'</li>',
|
||||
'<li>',
|
||||
__("Stock Ledger Entries and GL Entries are reposted for the selected Purchase Receipts"),
|
||||
'</li>',
|
||||
'</ul>',
|
||||
'</td></tr>',
|
||||
'</table>'].join("\n");
|
||||
|
||||
set_field_options("landed_cost_help", help_content);
|
||||
},
|
||||
|
||||
get_items_from_purchase_receipts: function() {
|
||||
var me = this;
|
||||
if(!this.frm.doc.landed_cost_purchase_receipts.length) {
|
||||
msgprint(__("Please enter Purchase Receipt first"));
|
||||
} else {
|
||||
return this.frm.call({
|
||||
doc: me.frm.doc,
|
||||
method: "get_items_from_purchase_receipts"
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
amount: function() {
|
||||
this.set_total_taxes_and_charges();
|
||||
this.set_applicable_charges_for_item();
|
||||
},
|
||||
|
||||
set_total_taxes_and_charges: function() {
|
||||
total_taxes_and_charges = 0.0;
|
||||
$.each(this.frm.doc.landed_cost_taxes_and_charges, function(i, d) {
|
||||
total_taxes_and_charges += flt(d.amount)
|
||||
});
|
||||
cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges);
|
||||
},
|
||||
|
||||
set_applicable_charges_for_item: function() {
|
||||
var me = this;
|
||||
if(this.frm.doc.landed_cost_taxes_and_charges.length) {
|
||||
var total_item_cost = 0.0;
|
||||
$.each(this.frm.doc.landed_cost_items, function(i, d) {
|
||||
total_item_cost += flt(d.amount)
|
||||
});
|
||||
|
||||
$.each(this.frm.doc.landed_cost_items, function(i, item) {
|
||||
item.applicable_charges = flt(item.amount) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
|
||||
});
|
||||
refresh_field("landed_cost_items");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher);
|
@ -0,0 +1,98 @@
|
||||
{
|
||||
"allow_import": 0,
|
||||
"autoname": "LCV.####",
|
||||
"creation": "2014-07-11 11:33:42.547339",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Transaction",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "landed_cost_purchase_receipts",
|
||||
"fieldtype": "Table",
|
||||
"label": "Purchase Receipts",
|
||||
"options": "Landed Cost Purchase Receipt",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "get_items_from_purchase_receipts",
|
||||
"fieldtype": "Button",
|
||||
"label": "Get Items From Purchase Receipts",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "landed_cost_items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Purchase Receipt Items",
|
||||
"no_copy": 1,
|
||||
"options": "Landed Cost Item",
|
||||
"permlevel": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "landed_cost_taxes_and_charges",
|
||||
"fieldtype": "Table",
|
||||
"label": "Taxes and Charges",
|
||||
"options": "Landed Cost Taxes and Charges",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "total_taxes_and_charges",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Taxes and Charges",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "landed_cost_help",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Landed Cost Help",
|
||||
"options": "",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"label": "Amended From",
|
||||
"no_copy": 1,
|
||||
"options": "Landed Cost Voucher",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-08-08 13:11:55.764550",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Voucher",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 1,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"export": 1,
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Material Manager",
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
106
erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
Normal file
106
erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
Normal file
@ -0,0 +1,106 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.model.document import Document
|
||||
|
||||
from erpnext.stock.utils import get_valuation_method
|
||||
from erpnext.stock.stock_ledger import get_previous_sle
|
||||
|
||||
class LandedCostVoucher(Document):
|
||||
def get_items_from_purchase_receipts(self):
|
||||
self.set("landed_cost_items", [])
|
||||
for pr in self.get("landed_cost_purchase_receipts"):
|
||||
pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description,
|
||||
pr_item.qty, pr_item.rate, pr_item.amount, pr_item.name
|
||||
from `tabPurchase Receipt Item` pr_item where parent = %s
|
||||
and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""",
|
||||
pr.purchase_receipt, as_dict=True)
|
||||
|
||||
for d in pr_items:
|
||||
item = self.append("landed_cost_items")
|
||||
item.item_code = d.item_code
|
||||
item.description = d.description
|
||||
item.qty = d.qty
|
||||
item.rate = d.rate
|
||||
item.amount = d.amount
|
||||
item.purchase_receipt = pr.purchase_receipt
|
||||
item.purchase_receipt_item = d.name
|
||||
|
||||
if self.get("landed_cost_taxes_and_charges"):
|
||||
self.set_applicable_charges_for_item()
|
||||
|
||||
|
||||
def validate(self):
|
||||
self.check_mandatory()
|
||||
self.validate_purchase_receipts()
|
||||
self.set_total_taxes_and_charges()
|
||||
if not self.get("landed_cost_items"):
|
||||
self.get_items_from_purchase_receipts()
|
||||
else:
|
||||
self.set_applicable_charges_for_item()
|
||||
|
||||
def check_mandatory(self):
|
||||
if not self.get("landed_cost_purchase_receipts"):
|
||||
frappe.throw(_("Please enter Purchase Receipts"))
|
||||
|
||||
if not self.get("landed_cost_taxes_and_charges"):
|
||||
frappe.throw(_("Please enter Taxes and Charges"))
|
||||
|
||||
def validate_purchase_receipts(self):
|
||||
purchase_receipts = []
|
||||
for d in self.get("landed_cost_purchase_receipts"):
|
||||
if frappe.db.get_value("Purchase Receipt", d.purchase_receipt, "docstatus") != 1:
|
||||
frappe.throw(_("Purchase Receipt must be submitted"))
|
||||
else:
|
||||
purchase_receipts.append(d.purchase_receipt)
|
||||
|
||||
for item in self.get("landed_cost_items"):
|
||||
if not item.purchase_receipt:
|
||||
frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button"))
|
||||
elif item.purchase_receipt not in purchase_receipts:
|
||||
frappe.throw(_("Item Row {0}: Purchase Receipt {1} does not exist in above 'Purchase Receipts' table")
|
||||
.format(item.idx, item.purchase_receipt))
|
||||
|
||||
def set_total_taxes_and_charges(self):
|
||||
self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("landed_cost_taxes_and_charges")])
|
||||
|
||||
def set_applicable_charges_for_item(self):
|
||||
total_item_cost = sum([flt(d.amount) for d in self.get("landed_cost_items")])
|
||||
|
||||
for item in self.get("landed_cost_items"):
|
||||
item.applicable_charges = flt(item.amount) * flt(self.total_taxes_and_charges) / flt(total_item_cost)
|
||||
|
||||
def on_submit(self):
|
||||
self.update_landed_cost()
|
||||
|
||||
def on_cancel(self):
|
||||
self.update_landed_cost()
|
||||
|
||||
def update_landed_cost(self):
|
||||
purchase_receipts = list(set([d.purchase_receipt for d in self.get("landed_cost_items")]))
|
||||
for purchase_receipt in purchase_receipts:
|
||||
pr = frappe.get_doc("Purchase Receipt", purchase_receipt)
|
||||
|
||||
# set landed cost voucher amount in pr item
|
||||
pr.set_landed_cost_voucher_amount()
|
||||
|
||||
# set valuation amount in pr item
|
||||
pr.update_valuation_rate("purchase_receipt_details")
|
||||
|
||||
# save will update landed_cost_voucher_amount and voucher_amount in PR,
|
||||
# as those fields are ellowed to edit after submit
|
||||
pr.save()
|
||||
|
||||
# update stock & gl entries for cancelled state of PR
|
||||
pr.docstatus = 2
|
||||
pr.update_stock()
|
||||
pr.make_gl_entries_on_cancel()
|
||||
|
||||
# update stock & gl entries for submit state of PR
|
||||
pr.docstatus = 1
|
||||
pr.update_stock()
|
||||
pr.make_gl_entries()
|
@ -0,0 +1,95 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import frappe
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||
import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records
|
||||
|
||||
|
||||
class TestLandedCostVoucher(unittest.TestCase):
|
||||
def test_landed_cost_voucher(self):
|
||||
set_perpetual_inventory(1)
|
||||
pr = frappe.copy_doc(pr_test_records[0])
|
||||
pr.submit()
|
||||
|
||||
bin_details = frappe.db.get_value("Bin", {"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "_Test Item"}, ["actual_qty", "stock_value"], as_dict=1)
|
||||
|
||||
self.submit_landed_cost_voucher(pr)
|
||||
|
||||
pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount")
|
||||
self.assertEquals(pr_lc_value, 25.0)
|
||||
|
||||
bin_details_after_lcv = frappe.db.get_value("Bin", {"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "_Test Item"}, ["actual_qty", "stock_value"], as_dict=1)
|
||||
|
||||
self.assertEqual(bin_details.actual_qty, bin_details_after_lcv.actual_qty)
|
||||
|
||||
self.assertEqual(bin_details_after_lcv.stock_value - bin_details.stock_value, 25.0)
|
||||
|
||||
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
stock_in_hand_account = pr.get("purchase_receipt_details")[0].warehouse
|
||||
fixed_asset_account = pr.get("purchase_receipt_details")[1].warehouse
|
||||
|
||||
|
||||
expected_values = {
|
||||
stock_in_hand_account: [400.0, 0.0],
|
||||
fixed_asset_account: [400.0, 0.0],
|
||||
"Stock Received But Not Billed - _TC": [0.0, 500.0],
|
||||
"Expenses Included In Valuation - _TC": [0.0, 300.0]
|
||||
}
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEquals(expected_values[gle.account][0], gle.debit)
|
||||
self.assertEquals(expected_values[gle.account][1], gle.credit)
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
def test_landed_cost_voucher_for_serialized_item(self):
|
||||
set_perpetual_inventory(1)
|
||||
frappe.db.sql("delete from `tabSerial No` where name in ('SN001', 'SN002', 'SN003', 'SN004', 'SN005')")
|
||||
|
||||
pr = frappe.copy_doc(pr_test_records[0])
|
||||
pr.purchase_receipt_details[0].item_code = "_Test Serialized Item"
|
||||
pr.purchase_receipt_details[0].serial_no = "SN001\nSN002\nSN003\nSN004\nSN005"
|
||||
pr.submit()
|
||||
|
||||
serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate")
|
||||
|
||||
self.submit_landed_cost_voucher(pr)
|
||||
|
||||
serial_no = frappe.db.get_value("Serial No", "SN001",
|
||||
["status", "warehouse", "purchase_rate"], as_dict=1)
|
||||
|
||||
self.assertEquals(serial_no.status, "Available")
|
||||
self.assertEquals(serial_no.purchase_rate - serial_no_rate, 5.0)
|
||||
self.assertEquals(serial_no.warehouse, "_Test Warehouse - _TC")
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
def submit_landed_cost_voucher(self, pr):
|
||||
lcv = frappe.new_doc("Landed Cost Voucher")
|
||||
lcv.company = "_Test Company"
|
||||
lcv.set("landed_cost_purchase_receipts", [{
|
||||
"purchase_receipt": pr.name,
|
||||
"supplier": pr.supplier,
|
||||
"posting_date": pr.posting_date,
|
||||
"grand_total": pr.grand_total
|
||||
}])
|
||||
lcv.set("landed_cost_taxes_and_charges", [{
|
||||
"description": "Insurance Charges",
|
||||
"account": "_Test Account Insurance Charges - _TC",
|
||||
"amount": 50.0
|
||||
}])
|
||||
|
||||
lcv.insert()
|
||||
lcv.submit()
|
||||
|
||||
|
||||
test_records = frappe.get_test_records('Landed Cost Voucher')
|
@ -1 +0,0 @@
|
||||
Tool to distribute costs as part of Item value after the Item has been received. This is typically in case where bills related to Items are received much later and for multiple Item. (specially Custom Duty)
|
@ -1 +0,0 @@
|
||||
from __future__ import unicode_literals
|
@ -1,46 +0,0 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
frappe.provide("erpnext.stock");
|
||||
frappe.require("assets/erpnext/js/controllers/stock_controller.js");
|
||||
|
||||
erpnext.stock.LandedCostWizard = erpnext.stock.StockController.extend({
|
||||
setup: function() {
|
||||
var me = this;
|
||||
this.frm.fields_dict.lc_pr_details.grid.get_field('purchase_receipt').get_query =
|
||||
function() {
|
||||
if(!me.frm.doc.company) msgprint(__("Please enter company first"));
|
||||
return {
|
||||
filters:[
|
||||
['Purchase Receipt', 'docstatus', '=', '1'],
|
||||
['Purchase Receipt', 'company', '=', me.frm.doc.company],
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
this.frm.fields_dict.landed_cost_details.grid.get_field('account_head').get_query = function() {
|
||||
if(!me.frm.doc.company) msgprint(__("Please enter company first"));
|
||||
return {
|
||||
filters:[
|
||||
['Account', 'group_or_ledger', '=', 'Ledger'],
|
||||
['Account', 'account_type', 'in', 'Tax, Chargeable'],
|
||||
['Account', 'company', '=', me.frm.doc.company]
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
this.frm.fields_dict.landed_cost_details.grid.get_field('cost_center').get_query =
|
||||
function() {
|
||||
if(!me.frm.doc.company) msgprint(__("Please enter company first"));
|
||||
return {
|
||||
filters:[
|
||||
['Cost Center', 'group_or_ledger', '=', 'Ledger'],
|
||||
['Cost Center', 'company', '=', me.frm.doc.company]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cur_frm.script_manager.make(erpnext.stock.LandedCostWizard);
|
@ -1,83 +0,0 @@
|
||||
{
|
||||
"creation": "2013-01-22 16:50:39.000000",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break0",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Select Purchase Receipts",
|
||||
"options": "Simple",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "lc_pr_details",
|
||||
"fieldtype": "Table",
|
||||
"label": "Landed Cost Purchase Receipts",
|
||||
"options": "Landed Cost Purchase Receipt",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break1",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Add Taxes and Charges",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "landed_cost_details",
|
||||
"fieldtype": "Table",
|
||||
"label": "Landed Cost Items",
|
||||
"options": "Landed Cost Item",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "update_landed_cost",
|
||||
"fieldtype": "Button",
|
||||
"label": "Update Landed Cost",
|
||||
"options": "update_landed_cost",
|
||||
"permlevel": 0
|
||||
}
|
||||
],
|
||||
"icon": "icon-magic",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"modified": "2013-12-20 19:23:18.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Wizard",
|
||||
"owner": "wasim@webnotestech.com",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Purchase Manager",
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Purchase User",
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
]
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
from frappe import msgprint, _
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
class LandedCostWizard(Document):
|
||||
|
||||
def update_landed_cost(self):
|
||||
"""
|
||||
Add extra cost and recalculate all values in pr,
|
||||
Recalculate valuation rate in all sle after pr posting date
|
||||
"""
|
||||
purchase_receipts = [row.purchase_receipt for row in
|
||||
self.get("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 validate_purchase_receipts(self, purchase_receipts):
|
||||
for pr in purchase_receipts:
|
||||
if frappe.db.get_value("Purchase Receipt", pr, "docstatus") != 1:
|
||||
frappe.throw(_("Purchase Receipt {0} is not submitted").format(pr))
|
||||
|
||||
def add_charges_in_pr(self, purchase_receipts):
|
||||
""" Add additional charges in selected pr proportionately"""
|
||||
total_amt = self.get_total_pr_amt(purchase_receipts)
|
||||
|
||||
for pr in purchase_receipts:
|
||||
pr_doc = frappe.get_doc('Purchase Receipt', pr)
|
||||
pr_items = pr_doc.get("purchase_tax_details")
|
||||
|
||||
for lc in self.get("landed_cost_details"):
|
||||
amt = flt(lc.amount) * flt(pr_doc.net_total)/ flt(total_amt)
|
||||
|
||||
matched_row = pr_doc.get("other_charges", {
|
||||
"category": "Valuation",
|
||||
"add_deduct_tax": "Add",
|
||||
"charge_type": "Actual",
|
||||
"account_head": lc.account_head
|
||||
})
|
||||
|
||||
if not matched_row: # add if not exists
|
||||
ch = pr_doc.append("other_charges")
|
||||
ch.category = 'Valuation'
|
||||
ch.add_deduct_tax = 'Add'
|
||||
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.docstatus = 1
|
||||
ch.db_insert()
|
||||
else: # overwrite if exists
|
||||
matched_row[0].rate = amt
|
||||
matched_row[0].tax_amount = amt
|
||||
matched_row[0].cost_center = lc.cost_center
|
||||
|
||||
pr_doc.run_method("validate")
|
||||
pr_doc._validate_mandatory()
|
||||
for d in pr_doc.get_all_children():
|
||||
d.db_update()
|
||||
|
||||
def get_total_pr_amt(self, purchase_receipts):
|
||||
return frappe.db.sql("""SELECT SUM(net_total) FROM `tabPurchase Receipt`
|
||||
WHERE name in (%s)""" % ', '.join(['%s']*len(purchase_receipts)),
|
||||
tuple(purchase_receipts))[0][0]
|
||||
|
||||
def cancel_pr(self, purchase_receipts):
|
||||
for pr in purchase_receipts:
|
||||
pr_doc = frappe.get_doc("Purchase Receipt", pr)
|
||||
|
||||
pr_doc.run_method("update_ordered_qty")
|
||||
|
||||
frappe.db.sql("""delete from `tabStock Ledger Entry`
|
||||
where voucher_type='Purchase Receipt' and voucher_no=%s""", pr)
|
||||
frappe.db.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_doc = frappe.get_doc("Purchase Receipt", pr)
|
||||
pr_doc.run_method("update_ordered_qty")
|
||||
pr_doc.run_method("update_stock")
|
||||
pr_doc.run_method("make_gl_entries")
|
@ -37,7 +37,7 @@ class PurchaseReceipt(BuyingController):
|
||||
|
||||
def onload(self):
|
||||
billed_qty = frappe.db.sql("""select sum(ifnull(qty, 0)) from `tabPurchase Invoice Item`
|
||||
where purchase_receipt=%s""", self.name)
|
||||
where purchase_receipt=%s and docstatus=1""", self.name)
|
||||
if billed_qty:
|
||||
total_qty = sum((item.qty for item in self.get("purchase_receipt_details")))
|
||||
self.get("__onload").billing_complete = (billed_qty[0][0] == total_qty)
|
||||
@ -67,9 +67,16 @@ class PurchaseReceipt(BuyingController):
|
||||
# sub-contracting
|
||||
self.validate_for_subcontracting()
|
||||
self.create_raw_materials_supplied("pr_raw_material_details")
|
||||
|
||||
self.set_landed_cost_voucher_amount()
|
||||
self.update_valuation_rate("purchase_receipt_details")
|
||||
|
||||
def set_landed_cost_voucher_amount(self):
|
||||
for d in self.get("purchase_receipt_details"):
|
||||
lc_voucher_amount = frappe.db.sql("""select sum(ifnull(applicable_charges, 0))
|
||||
from `tabLanded Cost Item`
|
||||
where docstatus = 1 and purchase_receipt_item = %s""", d.name)
|
||||
d.landed_cost_voucher_amount = lc_voucher_amount[0][0] if lc_voucher_amount else 0.0
|
||||
|
||||
def validate_rejected_warehouse(self):
|
||||
for d in self.get("purchase_receipt_details"):
|
||||
if flt(d.rejected_qty) and not d.rejected_warehouse:
|
||||
@ -143,7 +150,7 @@ class PurchaseReceipt(BuyingController):
|
||||
"warehouse": d.rejected_warehouse,
|
||||
"actual_qty": flt(d.rejected_qty) * flt(d.conversion_factor),
|
||||
"serial_no": cstr(d.rejected_serial_no).strip(),
|
||||
"incoming_rate": d.valuation_rate
|
||||
"incoming_rate": 0.0
|
||||
}))
|
||||
|
||||
self.bk_flush_supp_wh(sl_entries)
|
||||
@ -265,7 +272,7 @@ class PurchaseReceipt(BuyingController):
|
||||
self.update_prevdoc_status()
|
||||
pc_obj.update_last_purchase_rate(self, 0)
|
||||
|
||||
self.make_cancel_gl_entries()
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
def get_current_stock(self):
|
||||
for d in self.get('pr_raw_material_details'):
|
||||
@ -277,10 +284,116 @@ class PurchaseReceipt(BuyingController):
|
||||
return frappe.get_doc('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")
|
||||
from erpnext.accounts.general_ledger import process_gl_map
|
||||
|
||||
gl_entries = super(PurchaseReceipt, self).get_gl_entries(warehouse_account, against_stock_account)
|
||||
return gl_entries
|
||||
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
|
||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||
|
||||
gl_entries = []
|
||||
warehouse_with_no_account = []
|
||||
negative_expense_to_be_booked = 0.0
|
||||
stock_items = self.get_stock_items()
|
||||
for d in self.get("purchase_receipt_details"):
|
||||
if d.item_code in stock_items and flt(d.valuation_rate) and flt(d.qty):
|
||||
if warehouse_account.get(d.warehouse):
|
||||
|
||||
# warehouse account
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": warehouse_account[d.warehouse],
|
||||
"against": stock_rbnb,
|
||||
"cost_center": d.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": flt(flt(d.valuation_rate) * flt(d.qty) * flt(d.conversion_factor),
|
||||
self.precision("valuation_rate", d))
|
||||
}))
|
||||
|
||||
# stock received but not billed
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": stock_rbnb,
|
||||
"against": warehouse_account[d.warehouse],
|
||||
"cost_center": d.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(d.base_amount, self.precision("base_amount", d))
|
||||
}))
|
||||
|
||||
negative_expense_to_be_booked += flt(d.item_tax_amount)
|
||||
|
||||
# Amount added through landed-cost-voucher
|
||||
if flt(d.landed_cost_voucher_amount):
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": expenses_included_in_valuation,
|
||||
"against": warehouse_account[d.warehouse],
|
||||
"cost_center": d.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(d.landed_cost_voucher_amount)
|
||||
}))
|
||||
|
||||
# sub-contracting warehouse
|
||||
if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": warehouse_account[self.supplier_warehouse],
|
||||
"against": warehouse_account[d.warehouse],
|
||||
"cost_center": d.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(d.rm_supp_cost)
|
||||
}))
|
||||
|
||||
elif d.warehouse not in warehouse_with_no_account or \
|
||||
d.rejected_warehouse not in warehouse_with_no_account:
|
||||
warehouse_with_no_account.append(d.warehouse)
|
||||
|
||||
# Cost center-wise amount breakup for other charges included for valuation
|
||||
valuation_tax = {}
|
||||
for tax in self.get("other_charges"):
|
||||
if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount):
|
||||
if not tax.cost_center:
|
||||
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
|
||||
valuation_tax.setdefault(tax.cost_center, 0)
|
||||
valuation_tax[tax.cost_center] += \
|
||||
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount)
|
||||
|
||||
if negative_expense_to_be_booked and valuation_tax:
|
||||
# Backward compatibility:
|
||||
# If expenses_included_in_valuation account has been credited in against PI
|
||||
# and charges added via Landed Cost Voucher,
|
||||
# post valuation related charges on "Stock Received But Not Billed"
|
||||
|
||||
negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi
|
||||
where docstatus = 1 and purchase_receipt=%s
|
||||
and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice'
|
||||
and voucher_no=pi.parent and account=%s)""", (self.name, expenses_included_in_valuation))
|
||||
|
||||
if negative_expense_booked_in_pi:
|
||||
expenses_included_in_valuation = stock_rbnb
|
||||
|
||||
against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
|
||||
total_valuation_amount = sum(valuation_tax.values())
|
||||
amount_including_divisional_loss = negative_expense_to_be_booked
|
||||
i = 1
|
||||
for cost_center, amount in valuation_tax.items():
|
||||
if i == len(valuation_tax):
|
||||
applicable_amount = amount_including_divisional_loss
|
||||
else:
|
||||
applicable_amount = negative_expense_to_be_booked * (amount / total_valuation_amount)
|
||||
amount_including_divisional_loss -= applicable_amount
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": expenses_included_in_valuation,
|
||||
"cost_center": cost_center,
|
||||
"credit": applicable_amount,
|
||||
"remarks": self.remarks or _("Accounting Entry for Stock"),
|
||||
"against": against_account
|
||||
})
|
||||
)
|
||||
|
||||
i += 1
|
||||
|
||||
if warehouse_with_no_account:
|
||||
frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
|
||||
"\n".join(warehouse_with_no_account))
|
||||
|
||||
return process_gl_map(gl_entries)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -55,7 +55,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
|
||||
set_perpetual_inventory()
|
||||
self.assertEqual(cint(frappe.defaults.get_global_default("auto_accounting_for_stock")), 1)
|
||||
|
||||
pr = frappe.copy_doc(test_records[0])
|
||||
pr.insert()
|
||||
pr.submit()
|
||||
@ -72,7 +71,8 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
expected_values = {
|
||||
stock_in_hand_account: [375.0, 0.0],
|
||||
fixed_asset_account: [375.0, 0.0],
|
||||
"Stock Received But Not Billed - _TC": [0.0, 750.0]
|
||||
"Stock Received But Not Billed - _TC": [0.0, 500.0],
|
||||
"Expenses Included In Valuation - _TC": [0.0, 250.0]
|
||||
}
|
||||
|
||||
for gle in gl_entries:
|
||||
@ -118,6 +118,28 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
self.assertFalse(frappe.db.get_value("Serial No", pr.get("purchase_receipt_details")[0].serial_no,
|
||||
"warehouse"))
|
||||
|
||||
def test_rejected_serial_no(self):
|
||||
pr = frappe.copy_doc(test_records[0])
|
||||
pr.get("purchase_receipt_details")[0].item_code = "_Test Serialized Item With Series"
|
||||
pr.get("purchase_receipt_details")[0].qty = 3
|
||||
pr.get("purchase_receipt_details")[0].rejected_qty = 2
|
||||
pr.get("purchase_receipt_details")[0].received_qty = 5
|
||||
pr.get("purchase_receipt_details")[0].rejected_warehouse = "_Test Rejected Warehouse - _TC"
|
||||
pr.insert()
|
||||
pr.submit()
|
||||
|
||||
accepted_serial_nos = pr.get("purchase_receipt_details")[0].serial_no.split("\n")
|
||||
self.assertEquals(len(accepted_serial_nos), 3)
|
||||
for serial_no in accepted_serial_nos:
|
||||
self.assertEquals(frappe.db.get_value("Serial No", serial_no, "warehouse"),
|
||||
pr.get("purchase_receipt_details")[0].warehouse)
|
||||
|
||||
rejected_serial_nos = pr.get("purchase_receipt_details")[0].rejected_serial_no.split("\n")
|
||||
self.assertEquals(len(rejected_serial_nos), 2)
|
||||
for serial_no in rejected_serial_nos:
|
||||
self.assertEquals(frappe.db.get_value("Serial No", serial_no, "warehouse"),
|
||||
pr.get("purchase_receipt_details")[0].rejected_warehouse)
|
||||
|
||||
def get_gl_entries(voucher_type, voucher_no):
|
||||
return frappe.db.sql("""select account, debit, credit
|
||||
from `tabGL Entry` where voucher_type=%s and voucher_no=%s
|
||||
|
@ -19,7 +19,8 @@
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"rate": 100.0,
|
||||
"tax_amount": 100.0
|
||||
"tax_amount": 100.0,
|
||||
"cost_center": "Main - _TC"
|
||||
},
|
||||
{
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
@ -30,7 +31,8 @@
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"rate": 120.0,
|
||||
"tax_amount": 120.0
|
||||
"tax_amount": 120.0,
|
||||
"cost_center": "Main - _TC"
|
||||
},
|
||||
{
|
||||
"account_head": "_Test Account Customs Duty - _TC",
|
||||
@ -41,7 +43,8 @@
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "other_charges",
|
||||
"rate": 150.0,
|
||||
"tax_amount": 150.0
|
||||
"tax_amount": 150.0,
|
||||
"cost_center": "Main - _TC"
|
||||
}
|
||||
],
|
||||
"posting_date": "2013-02-12",
|
||||
@ -61,7 +64,8 @@
|
||||
"rejected_qty": 0.0,
|
||||
"stock_uom": "Nos",
|
||||
"uom": "_Test UOM",
|
||||
"warehouse": "_Test Warehouse - _TC"
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"cost_center": "Main - _TC"
|
||||
},
|
||||
{
|
||||
"base_amount": 250.0,
|
||||
@ -77,7 +81,8 @@
|
||||
"rejected_qty": 0.0,
|
||||
"stock_uom": "Nos",
|
||||
"uom": "_Test UOM",
|
||||
"warehouse": "_Test Warehouse 1 - _TC"
|
||||
"warehouse": "_Test Warehouse 1 - _TC",
|
||||
"cost_center": "Main - _TC"
|
||||
}
|
||||
],
|
||||
"supplier": "_Test Supplier"
|
||||
@ -109,7 +114,8 @@
|
||||
"rejected_qty": 0.0,
|
||||
"stock_uom": "Nos",
|
||||
"uom": "_Test UOM",
|
||||
"warehouse": "_Test Warehouse - _TC"
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"cost_center": "Main - _TC"
|
||||
}
|
||||
],
|
||||
"supplier": "_Test Supplier",
|
||||
|
@ -426,6 +426,7 @@
|
||||
"fieldname": "rejected_serial_no",
|
||||
"fieldtype": "Text",
|
||||
"label": "Rejected Serial No",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
@ -502,6 +503,17 @@
|
||||
"width": "150px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "landed_cost_voucher_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Landed Cost Voucher Amount",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "valuation_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
@ -545,7 +557,7 @@
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2014-07-24 05:56:12.997533",
|
||||
"modified": "2014-08-11 12:48:13.509109",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt Item",
|
||||
|
@ -295,19 +295,30 @@ def make_serial_no(serial_no, sle):
|
||||
return sr.name
|
||||
|
||||
def update_serial_nos_after_submit(controller, parentfield):
|
||||
stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no
|
||||
stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no, actual_qty, warehouse
|
||||
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
||||
(controller.doctype, controller.name), as_dict=True)
|
||||
|
||||
if not stock_ledger_entries: return
|
||||
|
||||
for d in controller.get(parentfield):
|
||||
serial_no = None
|
||||
update_rejected_serial_nos = True if (controller.doctype=="Purchase Receipt" and d.rejected_qty) else False
|
||||
accepted_serial_nos_updated = False
|
||||
warehouse = d.t_warehouse if controller.doctype == "Stock Entry" else d.warehouse
|
||||
|
||||
for sle in stock_ledger_entries:
|
||||
if sle.voucher_detail_no==d.name:
|
||||
serial_no = sle.serial_no
|
||||
break
|
||||
|
||||
if d.serial_no != serial_no:
|
||||
d.serial_no = serial_no
|
||||
frappe.db.set_value(d.doctype, d.name, "serial_no", serial_no)
|
||||
if not accepted_serial_nos_updated and d.qty and abs(sle.actual_qty)==d.qty \
|
||||
and sle.warehouse == warehouse and sle.serial_no != d.serial_no:
|
||||
d.serial_no = sle.serial_no
|
||||
frappe.db.set_value(d.doctype, d.name, "serial_no", sle.serial_no)
|
||||
accepted_serial_nos_updated = True
|
||||
if not update_rejected_serial_nos:
|
||||
break
|
||||
elif update_rejected_serial_nos and abs(sle.actual_qty)==d.rejected_qty \
|
||||
and sle.warehouse == d.rejected_warehouse and sle.serial_no != d.rejected_serial_no:
|
||||
d.rejected_serial_no = sle.serial_no
|
||||
frappe.db.set_value(d.doctype, d.name, "rejected_serial_no", sle.serial_no)
|
||||
update_rejected_serial_nos = False
|
||||
if accepted_serial_nos_updated:
|
||||
break
|
||||
|
@ -65,7 +65,7 @@ class StockEntry(StockController):
|
||||
def on_cancel(self):
|
||||
self.update_stock_ledger()
|
||||
self.update_production_order()
|
||||
self.make_cancel_gl_entries()
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
def validate_fiscal_year(self):
|
||||
from erpnext.accounts.utils import validate_fiscal_year
|
||||
|
@ -27,7 +27,7 @@ class StockReconciliation(StockController):
|
||||
|
||||
def on_cancel(self):
|
||||
self.delete_and_repost_sle()
|
||||
self.make_cancel_gl_entries()
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
def validate_data(self):
|
||||
if not self.reconciliation_json:
|
||||
|
@ -11,6 +11,12 @@
|
||||
"doctype": "Warehouse",
|
||||
"warehouse_name": "_Test Warehouse 1"
|
||||
},
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"create_account_under": "Stock Assets - _TC",
|
||||
"doctype": "Warehouse",
|
||||
"warehouse_name": "_Test Rejected Warehouse"
|
||||
},
|
||||
{
|
||||
"company": "_Test Company 1",
|
||||
"create_account_under": "Stock Assets - _TC1",
|
||||
|
@ -16,3 +16,6 @@ class TestAddress(unittest.TestCase):
|
||||
address = frappe.get_list("Address")[0].name
|
||||
display = get_address_display(frappe.get_doc("Address", address).as_dict())
|
||||
self.assertTrue(display)
|
||||
|
||||
|
||||
test_dependencies = ["Address Template"]
|
||||
|
Loading…
Reference in New Issue
Block a user