Merge branch 'nabinhait-landed_cost' into develop

This commit is contained in:
Anand Doshi 2014-08-11 18:04:08 +05:30
commit c7105a1a79
34 changed files with 864 additions and 310 deletions

View File

@ -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],

View File

@ -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

View File

@ -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):

View File

@ -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"
}
]

View File

@ -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):

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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")

View File

@ -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):
"""

View File

@ -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": []
}

View File

@ -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": []
}

View File

@ -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"
}

View File

@ -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

View 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);

View File

@ -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"
}

View 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()

View File

@ -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')

View File

@ -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)

View File

@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@ -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);

View File

@ -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
}
]
}

View File

@ -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")

View File

@ -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()

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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",

View File

@ -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"]