Warehouse Account Linking (#9292)

* [enhance] Added account in the warehouse

* documentation

* patch to move account head from account to warehouse

* fixed test cases

* Fixes in warehouse-account linking

* minor fix in test case
This commit is contained in:
Nabin Hait 2017-06-15 11:09:27 +05:30 committed by GitHub
parent 0b8e19b5d8
commit 6d7b0ce794
43 changed files with 336 additions and 397 deletions

View File

@ -1,5 +1,6 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"beta": 0,
@ -402,35 +403,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -545,18 +517,18 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-money",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-17 16:22:49.249075",
"modified": "2017-04-21 17:22:41.150984",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",

View File

@ -35,7 +35,6 @@ class Account(Document):
self.validate_group_or_ledger()
self.set_root_and_report_type()
self.validate_mandatory()
self.validate_warehouse_account()
self.validate_frozen_accounts_modifier()
self.validate_balance_must_be_debit_or_credit()
self.validate_account_currency()
@ -162,46 +161,6 @@ class Account(Document):
if not self.report_type:
throw(_("Report Type is mandatory"))
def validate_warehouse_account(self):
'''If perpetual inventory is set, and warehouse is linked,
the account balance and stock balance as of now must always match.
'''
from erpnext.accounts.utils import get_balance_on
from erpnext.stock.utils import get_stock_value_on
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
return
if self.account_type == "Stock":
if self.is_group == 0 and not self.warehouse:
frappe.throw(_("Warehouse is mandatory for non group Accounts of type Stock"))
if self.warehouse:
# company must be same
if frappe.get_value('Warehouse', self.warehouse, 'company') != self.company:
frappe.throw(_("Warehouse company must be same as Account company"))
# balance must be same
stock_balance = get_stock_value_on(self.warehouse)
if self.is_new():
account_balance = 0.0
else:
account_balance = get_balance_on(self.name)
if account_balance != stock_balance:
frappe.throw(_('Account balance ({0}) for {1} and stock value ({2}) for warehouse {3} must be same')
.format(fmt_money(account_balance, currency=self.account_currency), self.name,
fmt_money(stock_balance, currency=self.account_currency), self.warehouse))
elif self.warehouse:
self.warehouse = None
def validate_warehouse(self, warehouse):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
if lft and rgt:
if frappe.db.sql_list("""select sle.name from `tabStock Ledger Entry` sle where exists (select wh.name from
tabWarehouse wh where lft >= %s and rgt <= %s and sle.warehouse = wh.name)""", (lft, rgt)):
throw(_("Stock entries exist against Warehouse {0}, hence you cannot re-assign or modify it").format(warehouse))
def update_nsm_model(self):
"""update lft, rgt indices for nested set model"""

View File

@ -37,16 +37,6 @@ frappe.treeview_settings["Account"] = {
},
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'),
depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'},
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse",
depends_on: 'eval:(!doc.is_group&&doc.account_type=="Stock")',
get_query: function() {
return {
filters:{
"company": frappe.treeview_settings.filters["company"]
}
}
}
},
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency",
description: __("Optional. Sets company's default currency, if not specified.")}
],

View File

@ -117,7 +117,7 @@ def get_charts_for_country(country):
def get_account_tree_from_existing_company(existing_company):
all_accounts = frappe.get_all('Account',
filters={'company': existing_company, "warehouse": ""},
filters={'company': existing_company},
fields = ["name", "account_name", "parent_account", "account_type",
"is_group", "root_type", "tax_rate"],
order_by="lft, rgt")

View File

@ -88,7 +88,6 @@
"Items Delivered to Customs on temprary Base": {}
},
"Stock in Hand": {
"is_group": 1,
"account_type": "Stock"
}
},

View File

@ -65,8 +65,9 @@
"account_type": "Fixed Asset"
},
"Stock": {
"account_type": "Stock",
"is_group": 1
"Stock in Hand": {
"account_type": "Stock"
}
},
"root_type": "Asset"
},

View File

@ -26,8 +26,9 @@
"Earnest Money": {}
},
"Stock Assets": {
"account_type": "Stock",
"is_group": 1
"Stock in Hand": {
"account_type": "Stock"
}
},
"Tax Assets": {
"is_group": 1

View File

@ -40,8 +40,9 @@
"Rental Deposits": {}
},
"Stock Assets": {
"is_group": 1,
"account_type": "Stock"
"Stock in Hand": {
"account_type": "Stock"
}
},
"Tax Assets": {
"GST-Input": {}

View File

@ -40,8 +40,9 @@
"Rental Deposits": {}
},
"Stock Assets": {
"account_type": "Stock",
"is_group": 1
"Stock in Hand": {
"account_type": "Stock"
}
},
"Tax Assets": {
"GST-Input": {}

View File

@ -30,8 +30,10 @@ def get():
_("Earnest Money"): {}
},
_("Stock Assets"): {
_("Stock In Hand"): {
"account_type": "Stock"
},
"account_type": "Stock",
"is_group": 1
},
_("Tax Assets"): {
"is_group": 1

View File

@ -3,6 +3,8 @@
from __future__ import unicode_literals
import frappe
from erpnext.stock import get_warehouse_account, get_company_default_inventory_account
def _make_test_records(verbose):
from frappe.test_runner import make_test_objects
@ -63,3 +65,24 @@ def _make_test_records(verbose):
} for account_name, parent_account, is_group, account_type, currency in accounts])
return test_objects
def get_inventory_account(company, warehouse=None):
account = None
if warehouse:
account = get_warehouse_account(warehouse, company)
else:
account = get_company_default_inventory_account(company)
return account
def create_account(**kwargs):
account = frappe.get_doc(dict(
doctype = "Account",
account_name = kwargs.get('account_name'),
account_type = kwargs.get('account_type'),
parent_account = kwargs.get('parent_account'),
company = kwargs.get('company')
))
account.save()
return account.name

View File

@ -20,15 +20,9 @@ class AccountsSettings(Document):
company.flags.ignore_permissions = True
company.save()
# Create account head for warehouses
warehouse_list = frappe.db.sql("""select name, company from tabWarehouse
where disabled=0""", as_dict=1)
warehouse_with_no_company = [d.name for d in warehouse_list if not d.company]
# validate warehouse linked to company
warehouse_with_no_company = frappe.db.sql_list("""select name from tabWarehouse
where disabled=0 and company is null or company = ''""")
if warehouse_with_no_company:
frappe.throw(_("Company is missing in warehouses {0}")
.format(comma_and(warehouse_with_no_company)))
for wh in warehouse_list:
wh_doc = frappe.get_doc("Warehouse", wh.name)
wh_doc.flags.ignore_permissions = True
wh_doc.save()
.format(comma_and(warehouse_with_no_company)))

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import unittest, frappe
from frappe.utils import flt
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.exceptions import InvalidAccountCurrency
@ -83,7 +84,8 @@ class TestJournalEntry(unittest.TestCase):
jv = frappe.copy_doc(test_records[0])
jv.get("accounts")[0].update({
"account": "_Test Warehouse - _TC",
"account": get_inventory_account('_Test Company'),
"company": "_Test Company",
"party_type": None,
"party": None
})

View File

@ -25,4 +25,5 @@ class ModeofPayment(Document):
for entry in self.accounts:
"""Error when Company of Ledger account doesn't match with Company Selected"""
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
frappe.throw(_("Account does not match with Company"))
frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
.format(entry.default_account, entry.company, self.name))

View File

@ -11,7 +11,7 @@ from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency, get_fiscal_year
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billed_amount_based_on_po
from erpnext.controllers.stock_controller import get_warehouse_account
from erpnext.stock import get_warehouse_account_map
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_for_closed_status
@ -172,7 +172,7 @@ class PurchaseInvoice(BuyingController):
if self.update_stock:
self.validate_item_code()
self.validate_warehouse()
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
for item in self.get("items"):
# in case of auto inventory accounting,
@ -185,7 +185,7 @@ class PurchaseInvoice(BuyingController):
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if self.update_stock:
item.expense_account = warehouse_account[item.warehouse]["name"]
item.expense_account = warehouse_account[item.warehouse]["account"]
else:
item.expense_account = stock_not_billed_account
@ -377,7 +377,7 @@ class PurchaseInvoice(BuyingController):
# item gl entries
stock_items = self.get_stock_items()
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
for item in self.get("items"):
if flt(item.base_net_amount):

View File

@ -12,6 +12,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
test_records as pr_test_records
from erpnext.exceptions import InvalidCurrency
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
from erpnext.accounts.doctype.account.test_account import get_inventory_account
test_dependencies = ["Item", "Cost Center"]
test_ignore = ["Serial No"]
@ -357,10 +358,11 @@ class TestPurchaseInvoice(unittest.TestCase):
order by account asc""", pi.name, as_dict=1)
self.assertTrue(gl_entries)
stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
expected_gl_entries = dict((d[0], d) for d in [
[pi.credit_to, 0.0, 250.0],
[pi.items[0].warehouse, 250.0, 0.0]
[stock_in_hand_account, 250.0, 0.0]
])
for i, gle in enumerate(gl_entries):
@ -377,12 +379,13 @@ class TestPurchaseInvoice(unittest.TestCase):
sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
group by account, voucher_no order by account asc;""", pi.name, as_dict=1)
stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
self.assertTrue(gl_entries)
expected_gl_entries = dict((d[0], d) for d in [
[pi.credit_to, 250.0, 250.0],
[pi.items[0].warehouse, 250.0, 0.0],
[stock_in_hand_account, 250.0, 0.0],
["Cash - _TC", 0.0, 250.0]
])

View File

@ -12,6 +12,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
from frappe.model.naming import make_autoname
from erpnext.accounts.doctype.account.test_account import get_inventory_account
class TestSalesInvoice(unittest.TestCase):
def make(self):
@ -582,7 +583,7 @@ class TestSalesInvoice(unittest.TestCase):
order by account asc, debit asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
stock_in_hand = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
stock_in_hand = get_inventory_account('_Test Company')
expected_gl_entries = sorted([
[si.debit_to, 630.0, 0.0],
@ -837,11 +838,11 @@ class TestSalesInvoice(unittest.TestCase):
["incoming_rate", "stock_value_difference"])
self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
stock_in_hand_account = get_inventory_account('_Test Company', si1.items[0].warehouse)
# Check gl entry
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
"voucher_no": si1.name, "account": "_Test Warehouse - _TC"}, "debit")
"voucher_no": si1.name, "account": stock_in_hand_account}, "debit")
self.assertEquals(gle_warehouse_amount, stock_value_difference)

View File

@ -104,7 +104,7 @@ def validate_account_for_auto_accounting_for_stock(gl_map):
if cint(frappe.db.get_single_value("Accounts Settings", "auto_accounting_for_stock")) \
and gl_map[0].voucher_type=="Journal Entry":
aii_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
where account_type = 'Stock' and (warehouse != '' and warehouse is not null) and is_group=0""")]
where account_type = 'Stock' and is_group=0""")]
for entry in gl_map:
if entry.account in aii_accounts:

View File

@ -216,7 +216,8 @@ def get_count_on(account, fieldname, date):
else:
dr_or_cr = "debit" if fieldname == "invoiced_amount" else "credit"
cr_or_dr = "credit" if fieldname == "invoiced_amount" else "debit"
select_fields = "ifnull(sum(credit-debit),0)" if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)"
select_fields = "ifnull(sum(credit-debit),0)" \
if fieldname == "invoiced_amount" else "ifnull(sum(debit-credit),0)"
if ((not gle.against_voucher) or (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or
(gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0)):
@ -224,8 +225,10 @@ def get_count_on(account, fieldname, date):
SELECT {0}
FROM `tabGL Entry` gle
WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s
and party = %(party)s and name != %(name)s""".format(select_fields),
{"date": date, "voucher_no": gle.voucher_no, "party": gle.party, "name": gle.name})[0][0]
and party = %(party)s and name != %(name)s"""
.format(select_fields),
{"date": date, "voucher_no": gle.voucher_no,
"party": gle.party, "name": gle.name})[0][0]
outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount
currency_precision = get_currency_precision() or 2
@ -519,20 +522,19 @@ def fix_total_debit_credit():
def get_stock_and_account_difference(account_list=None, posting_date=None):
from erpnext.stock.utils import get_stock_value_on
from erpnext.stock import get_warehouse_account_map
if not posting_date: posting_date = nowdate()
difference = {}
warehouse_account = get_warehouse_account_map()
account_warehouse = dict(frappe.db.sql("""select name, warehouse from tabAccount
where account_type = 'Stock' and (warehouse is not null and warehouse != '') and is_group=0
and name in (%s)""" % ', '.join(['%s']*len(account_list)), account_list))
for account, warehouse in account_warehouse.items():
account_balance = get_balance_on(account, posting_date, in_account_currency=False)
stock_value = get_stock_value_on(warehouse, posting_date)
if abs(flt(stock_value) - flt(account_balance)) > 0.005:
difference.setdefault(account, flt(stock_value) - flt(account_balance))
for warehouse, account_data in warehouse_account.items():
if account_data.get('account') in account_list:
account_balance = get_balance_on(account_data.get('account'), posting_date, in_account_currency=False)
stock_value = get_stock_value_on(warehouse, posting_date)
if abs(flt(stock_value) - flt(account_balance)) > 0.005:
difference.setdefault(account, flt(stock_value) - flt(account_balance))
return difference

View File

@ -274,7 +274,9 @@ class AccountsController(TransactionBase):
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
if self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
if gl_dict.account and self.doctype not in ["Journal Entry",
"Period Closing Voucher", "Payment Entry"]:
self.validate_account_currency(gl_dict.account, account_currency)
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency)

View File

@ -10,6 +10,7 @@ from erpnext.accounts.utils import get_fiscal_year
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.stock import get_warehouse_account_map
class StockController(AccountsController):
def validate(self):
@ -21,7 +22,7 @@ class StockController(AccountsController):
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
if self.docstatus==1:
if not gl_entries:
@ -37,7 +38,7 @@ class StockController(AccountsController):
default_cost_center=None):
if not warehouse_account:
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
sle_map = self.get_stock_ledger_details()
voucher_details = self.get_voucher_details(default_expense_account, default_cost_center, sle_map)
@ -66,7 +67,7 @@ class StockController(AccountsController):
sle = self.update_stock_ledger_entries(sle)
gl_list.append(self.get_gl_dict({
"account": warehouse_account[sle.warehouse]["name"],
"account": warehouse_account[sle.warehouse]["account"],
"against": item_row.expense_account,
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
@ -76,7 +77,7 @@ class StockController(AccountsController):
# to target warehouse / expense account
gl_list.append(self.get_gl_dict({
"account": item_row.expense_account,
"against": warehouse_account[sle.warehouse]["name"],
"against": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2),
@ -88,10 +89,7 @@ class StockController(AccountsController):
if warehouse_with_no_account:
for wh in warehouse_with_no_account:
if frappe.db.get_value("Warehouse", wh, "company"):
frappe.throw(_("Warehouse {0} is not linked to any account, please create/link the corresponding (Asset) account for the warehouse.").format(wh))
msgprint(_("No accounting entries for the following warehouses") + ": \n" +
"\n".join(warehouse_with_no_account))
frappe.throw(_("Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}.").format(wh, self.company))
return process_gl_map(gl_list)
@ -341,7 +339,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
if not warehouse_account:
warehouse_account = get_warehouse_account()
warehouse_account = get_warehouse_account_map()
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
@ -405,22 +403,4 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries
def get_warehouse_account():
if not frappe.flags.warehouse_account_map or frappe.flags.in_test:
warehouse_account = frappe._dict()
for d in frappe.db.sql("""select
warehouse, name, account_currency
from
tabAccount
where
account_type = 'Stock'
and (warehouse is not null and warehouse != '')
and is_group=0 """, as_dict=1):
warehouse_account.setdefault(d.warehouse, d)
frappe.flags.warehouse_account_map = warehouse_account
return frappe.flags.warehouse_account_map
return gl_entries

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -640,8 +640,8 @@ attach them to the start of each source file to most effectively state
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.</p>
<pre><code> &lt;one line to give the program's name and a brief idea of what it does.&gt;
Copyright (C) &lt;year&gt; &lt;name of author&gt;
<pre><code> &lt;one line="" to="" give="" the="" program's="" name="" and="" a="" brief="" idea="" of="" what="" it="" does.=""&gt;
Copyright (C) &lt;year&gt; &lt;name of="" author=""&gt;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by

View File

@ -6,21 +6,28 @@ On receipt of items in a particular warehouse, the balance in the Warehouse Acco
##Activation
1. Setup the following default accounts for each Company. These accounts are created automatically in the new ERPNext accounts.
* Stock Received But Not Billed
* Stock Adjustment Account
* Expenses Included In Valuation
* Cost Center
2. Ensure each Warehouse is an Account in the Chart of Accounts master. As per the default configuration, Accounts for Warehouse are created under `Assets > Current Asset > Stock Assets > (Warehouse)`
3. Activate Perpetual Inventory
* Activate Perpetual Inventory
> Explore > Accounts > Accounts Settings > "Make Accounting Entry For Every Stock Movement"
<img class="screenshot" alt="Perpetual Inventory" src="{{docs_base_url}}/assets/img/accounts/perpetual-1.png">
* Setup the following default accounts for each Company. These accounts are created automatically in the new ERPNext accounts.
* Default Inventory Account
* Stock Received But Not Billed
* Stock Adjustment Account
* Expenses Included In Valuation
* Cost Center
<img class="screenshot" alt="Perpetual Inventory" src="{{docs_base_url}}/assets/img/accounts/company_default_inventory_account.png">
* If user wants to set an individual account for each warehouse, create account head for each account under `Assets > Current Asset > Stock Assets > (Warehouse)` and set it on the respective warehouse under field 'Account'.
<img class="screenshot" alt="Perpetual Inventory" src="{{docs_base_url}}/assets/img/accounts/inventory_account.png">
* For stock transactions, general ledger entries made against the account head set on the warehouse, if user had not set the account for the warhouse then system gets the account head from the parent warehouse. If account had not set for parent warehouse then system gets the account(Default Inventory Account) from the company master.
* * *
##Example

View File

@ -8,7 +8,7 @@ erpnext.patches.v4_0.apply_user_permissions
erpnext.patches.v4_0.move_warehouse_user_to_restrictions
erpnext.patches.v4_0.global_defaults_to_system_settings
erpnext.patches.v4_0.update_incharge_name_to_sales_person_in_maintenance_schedule
execute:frappe.reload_doc('stock', 'doctype', 'warehouse')
execute:frappe.reload_doc('stock', 'doctype', 'warehouse') # 2017-04-24
execute:frappe.reload_doc('accounts', 'doctype', 'sales_invoice') # 2016-08-31
execute:frappe.reload_doc('selling', 'doctype', 'sales_order') # 2014-01-29
execute:frappe.reload_doc('selling', 'doctype', 'quotation') # 2014-01-29
@ -401,3 +401,4 @@ erpnext.patches.v8_0.delete_schools_depricated_doctypes
erpnext.patches.v8_0.update_customer_pos_id
erpnext.patches.v8_0.rename_items_in_status_field_of_material_request
erpnext.patches.v8_0.delete_bin_indexes
erpnext.patches.v8_0.move_account_head_from_account_to_warehouse_for_inventory

View File

@ -90,8 +90,7 @@ def create_default_warehouse_group(company=None, stock_account_group=None, ignor
"warehouse_name": _("All Warehouses"),
"is_group": 1,
"company": company.name if company else "",
"parent_warehouse": "",
"create_account_under": stock_account_group
"parent_warehouse": ""
})
if ignore_mandatory:

View File

@ -4,7 +4,8 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint
from erpnext.controllers.stock_controller import get_warehouse_account, update_gl_entries_after
from erpnext.stock import get_warehouse_account_map
from erpnext.controllers.stock_controller import update_gl_entries_after
def execute():
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
@ -13,7 +14,7 @@ def execute():
frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
frappe.reload_doctype("Purchase Invoice")
wh_account = get_warehouse_account()
wh_account = get_warehouse_account_map()
for pi in frappe.get_all("Purchase Invoice", filters={"docstatus": 1, "update_stock": 1}):
pi_doc = frappe.get_doc("Purchase Invoice", pi.name)

View File

@ -0,0 +1,15 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doctype("Warehouse")
frappe.db.sql("""
update
`tabWarehouse`
set
account = (select name from `tabAccount`
where account_type = 'Stock' and
warehouse = `tabWarehouse`.name and is_group = 0)""")

View File

@ -15,7 +15,7 @@ frappe.ui.form.on("Company", {
frm.toggle_display('address_html', !frm.doc.__islocal);
if(!frm.doc.__islocal) {
frappe.geo.render_address_and_contact(frm);
frappe.contacts.render_address_and_contact(frm);
frm.toggle_enable("default_currency", (frm.doc.__onload &&
!frm.doc.__onload.transactions_exist));
@ -148,6 +148,7 @@ erpnext.company.setup_queries = function(frm) {
{"root_type": "Asset", "account_type": "Accumulated Depreciation"}],
["depreciation_expense_account", {"root_type": "Expense", "account_type": "Depreciation"}],
["disposal_account", {"report_type": "Profit and Loss"}],
["default_inventory_account", {"account_type": "Stock"}],
["cost_center", {}],
["round_off_cost_center", {}],
["depreciation_cost_center", {}]

View File

@ -1128,20 +1128,21 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "stock_received_but_not_billed",
"fieldname": "default_inventory_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Stock Received But Not Billed",
"label": "Default Inventory Account",
"length": 0,
"no_copy": 1,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@ -1213,6 +1214,35 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "stock_received_but_not_billed",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Stock Received But Not Billed",
"length": 0,
"no_copy": 1,
"options": "Account",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -1747,7 +1777,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-05-09 11:06:33.629948",
"modified": "2017-06-14 11:06:33.629948",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",

View File

@ -50,9 +50,11 @@ class Company(Document):
frappe.throw(_("Abbreviation already used for another company"))
def validate_default_accounts(self):
for field in ["default_bank_account", "default_cash_account", "default_receivable_account", "default_payable_account",
"default_expense_account", "default_income_account", "stock_received_but_not_billed",
"stock_adjustment_account", "expenses_included_in_valuation", "default_payroll_payable_account"]:
for field in ["default_bank_account", "default_cash_account",
"default_receivable_account", "default_payable_account",
"default_expense_account", "default_income_account",
"stock_received_but_not_billed", "stock_adjustment_account",
"expenses_included_in_valuation", "default_payroll_payable_account"]:
if self.get(field):
for_company = frappe.db.get_value("Account", self.get(field), "company")
if for_company != self.name:
@ -111,8 +113,7 @@ class Company(Document):
"is_group": wh_detail["is_group"],
"company": self.name,
"parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr) \
if not wh_detail["is_group"] else "",
"create_account_under": stock_group
if not wh_detail["is_group"] else ""
})
warehouse.flags.ignore_permissions = True
warehouse.insert()
@ -147,6 +148,7 @@ class Company(Document):
if cint(frappe.db.get_single_value("Accounts Settings", "auto_accounting_for_stock")):
self._set_default_account("stock_received_but_not_billed", "Stock Received But Not Billed")
self._set_default_account("default_inventory_account", "Stock")
self._set_default_account("stock_adjustment_account", "Stock Adjustment")
self._set_default_account("expenses_included_in_valuation", "Expenses Included In Valuation")
self._set_default_account("default_expense_account", "Cost of Goods Sold")

View File

@ -74,7 +74,7 @@ class TestCompany(unittest.TestCase):
"company": template,
"account_type": account_type
}
if account_type in ["Bank", "Cash", "Stock"]:
if account_type in ["Bank", "Cash"]:
filters["is_group"] = 1
self.assertTrue(frappe.get_all("Account", filters))

View File

@ -1,4 +1,6 @@
from __future__ import unicode_literals
import frappe
install_docs = [
{"doctype":"Role", "role_name":"Stock Manager", "name":"Stock Manager"},
{"doctype":"Role", "role_name":"Item Manager", "name":"Item Manager"},
@ -8,3 +10,39 @@ install_docs = [
{"doctype":"Item Group", "item_group_name":"Default",
"parent_item_group":"All Item Groups", "is_group": 0},
]
def get_warehouse_account_map():
if not frappe.flags.warehouse_account_map or frappe.flags.in_test:
warehouse_account = frappe._dict()
for d in frappe.get_all('Warehouse', filters = {"is_group": 0},
fields = ["name", "account", "parent_warehouse", "company"]):
if not d.account:
d.account = get_warehouse_account(d.name, d.company)
if d.account:
d.account_currency = frappe.db.get_value('Account', d.account, 'account_currency')
warehouse_account.setdefault(d.name, d)
frappe.flags.warehouse_account_map = warehouse_account
return frappe.flags.warehouse_account_map
def get_warehouse_account(warehouse, company):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
account = frappe.db.sql("""
select
account from `tabWarehouse`
where
lft <= %s and rgt >= %s and company = %s
and account is not null and ifnull(account, '') !=''
order by lft desc limit 1""", (lft, rgt, company), as_list=1)
account = account[0][0] if account else None
if not account:
account = get_company_default_inventory_account(company)
return account
def get_company_default_inventory_account(company):
return frappe.db.get_value('Company', company, 'default_inventory_account')

View File

@ -19,8 +19,19 @@ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, SerialNoWa
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \
import create_stock_reconciliation, set_valuation_method
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
class TestDeliveryNote(unittest.TestCase):
def tearDown(self):
target_warehouse = "_Test Warehouse 1 - _TC"
company = "_Test Company"
if not frappe.db.exists("Account", target_warehouse):
parent_account = frappe.db.get_value('Account',
{'company': company, 'is_group':1, 'account_type': 'Stock'},'name')
account = create_account(account_name="_Test Warehouse 1", \
account_type="Stock", parent_account= parent_account, company=company)
frappe.db.set_value('Warehouse', target_warehouse, 'account', account)
def test_over_billing_against_dn(self):
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
@ -63,7 +74,7 @@ class TestDeliveryNote(unittest.TestCase):
make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100)
stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
stock_in_hand_account = get_inventory_account('_Test Company')
prev_bal = get_balance_on(stock_in_hand_account)
dn = create_delivery_note()
@ -113,7 +124,7 @@ class TestDeliveryNote(unittest.TestCase):
make_stock_entry(item_code="_Test Item Home Desktop 100",
target="_Test Warehouse - _TC", qty=10, basic_rate=100)
stock_in_hand_account = frappe.db.get_value("Account", {"warehouse": "_Test Warehouse - _TC"})
stock_in_hand_account = get_inventory_account('_Test Company')
prev_bal = get_balance_on(stock_in_hand_account)
dn = create_delivery_note(item_code="_Test Product Bundle Item")
@ -212,9 +223,10 @@ class TestDeliveryNote(unittest.TestCase):
["incoming_rate", "stock_value_difference"])
self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
stock_in_hand_account = get_inventory_account('_Test Company', dn1.items[0].warehouse)
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
"voucher_no": dn1.name, "account": "_Test Warehouse - _TC"}, "debit")
"voucher_no": dn1.name, "account": stock_in_hand_account}, "debit")
self.assertEquals(gle_warehouse_amount, stock_value_difference)
@ -250,10 +262,11 @@ class TestDeliveryNote(unittest.TestCase):
["incoming_rate", "stock_value_difference"])
self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
stock_in_hand_account = get_inventory_account('_Test Company', dn1.items[0].warehouse)
# Check gl entry for warehouse
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
"voucher_no": dn1.name, "account": "_Test Warehouse - _TC"}, "debit")
"voucher_no": dn1.name, "account": stock_in_hand_account}, "debit")
self.assertEquals(gle_warehouse_amount, stock_value_difference)
@ -282,10 +295,11 @@ class TestDeliveryNote(unittest.TestCase):
["incoming_rate", "stock_value_difference"])
self.assertEquals(incoming_rate, 100)
stock_in_hand_account = get_inventory_account('_Test Company', dn1.items[0].warehouse)
# Check gl entry for warehouse
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
"voucher_no": dn1.name, "account": "_Test Warehouse - _TC"}, "debit")
"voucher_no": dn1.name, "account": stock_in_hand_account}, "debit")
self.assertEquals(gle_warehouse_amount, 1400)
@ -339,7 +353,6 @@ class TestDeliveryNote(unittest.TestCase):
target=warehouse, qty=100, rate=100)
opening_qty_test_warehouse_1 = get_qty_after_transaction(warehouse="_Test Warehouse 1 - _TC")
dn = create_delivery_note(item_code="_Test Product Bundle Item",
qty=5, rate=500, target_warehouse="_Test Warehouse 1 - _TC", do_not_submit=True)
@ -390,7 +403,7 @@ class TestDeliveryNote(unittest.TestCase):
and warehouse='_Test Warehouse - _TC'""", dn.name)[0][0])
expected_values = {
"_Test Warehouse - _TC": [0.0, stock_value_difference],
"Stock In Hand - _TC": [0.0, stock_value_difference],
"_Test Warehouse 1 - _TC": [stock_value_difference, 0.0]
}
for i, gle in enumerate(gl_entries):

View File

@ -9,6 +9,7 @@ from frappe.utils import flt
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records, make_purchase_receipt
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.account.test_account import get_inventory_account
class TestLandedCostVoucher(unittest.TestCase):
def test_landed_cost_voucher(self):
@ -46,16 +47,23 @@ class TestLandedCostVoucher(unittest.TestCase):
self.assertTrue(gl_entries)
stock_in_hand_account = pr.get("items")[0].warehouse
fixed_asset_account = pr.get("items")[1].warehouse
stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
fixed_asset_account = get_inventory_account(pr.company, pr.get("items")[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]
}
if stock_in_hand_account == fixed_asset_account:
expected_values = {
stock_in_hand_account: [800.0, 0.0],
"Stock Received But Not Billed - _TC": [0.0, 500.0],
"Expenses Included In Valuation - _TC": [0.0, 300.0]
}
else:
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)
@ -99,9 +107,10 @@ class TestLandedCostVoucher(unittest.TestCase):
gl_entries = get_gl_entries("Purchase Invoice", pi.name)
self.assertTrue(gl_entries)
stock_in_hand_account = get_inventory_account(pi.company, pi.get("items")[0].warehouse)
expected_values = {
pi.get("items")[0].warehouse: [300.0, 0.0],
stock_in_hand_account: [300.0, 0.0],
"Creditors - _TC": [0.0, 250.0],
"Expenses Included In Valuation - _TC": [0.0, 50.0]
}

View File

@ -189,7 +189,7 @@ class PurchaseReceipt(BuyingController):
if not stock_value_diff:
continue
gl_entries.append(self.get_gl_dict({
"account": warehouse_account[d.warehouse]["name"],
"account": warehouse_account[d.warehouse]["account"],
"against": stock_rbnb,
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@ -200,7 +200,7 @@ class PurchaseReceipt(BuyingController):
stock_rbnb_currency = get_account_currency(stock_rbnb)
gl_entries.append(self.get_gl_dict({
"account": stock_rbnb,
"against": warehouse_account[d.warehouse]["name"],
"against": warehouse_account[d.warehouse]["account"],
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(d.base_net_amount, d.precision("base_net_amount")),
@ -214,7 +214,7 @@ class PurchaseReceipt(BuyingController):
if flt(d.landed_cost_voucher_amount):
gl_entries.append(self.get_gl_dict({
"account": expenses_included_in_valuation,
"against": warehouse_account[d.warehouse]["name"],
"against": warehouse_account[d.warehouse]["account"],
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(d.landed_cost_voucher_amount),
@ -225,7 +225,7 @@ class PurchaseReceipt(BuyingController):
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]["name"],
"against": warehouse_account[d.warehouse]["name"],
"against": warehouse_account[d.warehouse]["account"],
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(d.rm_supp_cost)
@ -246,7 +246,7 @@ class PurchaseReceipt(BuyingController):
gl_entries.append(self.get_gl_dict({
"account": loss_account,
"against": warehouse_account[d.warehouse]["name"],
"against": warehouse_account[d.warehouse]["account"],
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": divisional_loss,

View File

@ -9,6 +9,7 @@ import frappe.defaults
from frappe.utils import cint, flt, cstr, today
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
from erpnext import set_perpetual_inventory
from erpnext.accounts.doctype.account.test_account import get_inventory_account
class TestPurchaseReceipt(unittest.TestCase):
def setUp(self):
@ -59,17 +60,22 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertTrue(gl_entries)
stock_in_hand_account = frappe.db.get_value("Account",
{"warehouse": pr.get("items")[0].warehouse})
fixed_asset_account = frappe.db.get_value("Account",
{"warehouse": pr.get("items")[1].warehouse})
stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
fixed_asset_account = get_inventory_account(pr.company, pr.get("items")[1].warehouse)
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, 500.0],
"Expenses Included In Valuation - _TC": [0.0, 250.0]
}
if stock_in_hand_account == fixed_asset_account:
expected_values = {
stock_in_hand_account: [750.0, 0.0],
"Stock Received But Not Billed - _TC": [0.0, 500.0],
"Expenses Included In Valuation - _TC": [0.0, 250.0]
}
else:
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, 500.0],
"Expenses Included In Valuation - _TC": [0.0, 250.0]
}
for gle in gl_entries:
self.assertEquals(expected_values[gle.account][0], gle.debit)
@ -141,9 +147,10 @@ class TestPurchaseReceipt(unittest.TestCase):
gl_entries = get_gl_entries("Purchase Receipt", return_pr.name)
self.assertTrue(gl_entries)
stock_in_hand_account = get_inventory_account(return_pr.company)
expected_values = {
"_Test Warehouse - _TC": [0.0, 100.0],
stock_in_hand_account: [0.0, 100.0],
"Stock Received But Not Billed - _TC": [100.0, 0.0],
}

View File

@ -13,6 +13,7 @@ from erpnext.stock.stock_ledger import get_previous_sle
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from frappe.tests.test_permissions import set_user_permission_doctypes
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.accounts.doctype.account.test_account import get_inventory_account
def get_sle(**args):
condition, values = "", []
@ -127,9 +128,7 @@ class TestStockEntry(unittest.TestCase):
mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC",
qty=50, basic_rate=100, expense_account="Stock Adjustment - _TC")
stock_in_hand_account = frappe.db.get_value("Account", {"account_type": "Stock",
"warehouse": mr.get("items")[0].t_warehouse})
stock_in_hand_account = get_inventory_account(mr.company, mr.get("items")[0].t_warehouse)
self.check_stock_ledger_entries("Stock Entry", mr.name,
[["_Test Item", "_Test Warehouse - _TC", 50.0]])
@ -160,9 +159,7 @@ class TestStockEntry(unittest.TestCase):
self.check_stock_ledger_entries("Stock Entry", mi.name,
[["_Test Item", "_Test Warehouse - _TC", -40.0]])
stock_in_hand_account = frappe.db.get_value("Account", {"account_type": "Stock",
"warehouse": "_Test Warehouse - _TC"})
stock_in_hand_account = get_inventory_account(mi.company, "_Test Warehouse - _TC")
stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
"voucher_no": mi.name}, "stock_value_difference"))
@ -192,21 +189,25 @@ class TestStockEntry(unittest.TestCase):
self.check_stock_ledger_entries("Stock Entry", mtn.name,
[["_Test Item", "_Test Warehouse - _TC", -45.0], ["_Test Item", "_Test Warehouse 1 - _TC", 45.0]])
stock_in_hand_account = frappe.db.get_value("Account", {"account_type": "Stock",
"warehouse": mtn.get("items")[0].s_warehouse})
fixed_asset_account = frappe.db.get_value("Account", {"account_type": "Stock",
"warehouse": mtn.get("items")[0].t_warehouse})
stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
"voucher_no": mtn.name, "warehouse": "_Test Warehouse - _TC"}, "stock_value_difference"))
self.check_gl_entries("Stock Entry", mtn.name,
sorted([
[stock_in_hand_account, 0.0, stock_value_diff],
[fixed_asset_account, stock_value_diff, 0.0],
])
)
stock_in_hand_account = get_inventory_account(mtn.company, mtn.get("items")[0].s_warehouse)
fixed_asset_account = get_inventory_account(mtn.company, mtn.get("items")[0].t_warehouse)
if stock_in_hand_account == fixed_asset_account:
# no gl entry as both source and target warehouse has linked to same account.
self.assertFalse(frappe.db.sql("""select * from `tabGL Entry`
where voucher_type='Stock Entry' and voucher_no=%s""", mtn.name))
else:
stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
"voucher_no": mtn.name, "warehouse": "_Test Warehouse - _TC"}, "stock_value_difference"))
self.check_gl_entries("Stock Entry", mtn.name,
sorted([
[stock_in_hand_account, 0.0, stock_value_diff],
[fixed_asset_account, stock_value_diff, 0.0],
])
)
mtn.cancel()
self.assertFalse(frappe.db.sql("""select * from `tabStock Ledger Entry`
@ -260,9 +261,7 @@ class TestStockEntry(unittest.TestCase):
repack.insert()
repack.submit()
stock_in_hand_account = frappe.db.get_value("Account", {"account_type": "Stock",
"warehouse": repack.get("items")[1].t_warehouse})
stock_in_hand_account = get_inventory_account(repack.company, repack.get("items")[1].t_warehouse)
rm_stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
"voucher_no": repack.name, "item_code": "_Test Item"}, "stock_value_difference"))

View File

@ -6,6 +6,7 @@ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from frappe.utils import cint
from erpnext import set_perpetual_inventory
from frappe.test_runner import make_test_records
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
import frappe
import unittest
@ -33,21 +34,16 @@ class TestWarehouse(unittest.TestCase):
def test_warehouse_renaming(self):
set_perpetual_inventory(1)
create_warehouse("Test Warehouse for Renaming 1")
self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 1 - _TC"))
self.assertTrue(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Renaming 1 - _TC"}))
account = get_inventory_account("_Test Company", "Test Warehouse for Renaming 1 - _TC")
self.assertTrue(frappe.db.get_value("Warehouse", filters={"account": account}))
# Rename with abbr
if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"):
frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC")
rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC")
self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 2 - _TC"))
self.assertTrue(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Renaming 2 - _TC"}))
self.assertFalse(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Renaming 1 - _TC"}))
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Renaming 1 - _TC"}))
# Rename without abbr
if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"):
@ -55,19 +51,14 @@ class TestWarehouse(unittest.TestCase):
rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3")
self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 3 - _TC"))
self.assertTrue(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Renaming 3 - _TC"}))
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Renaming 1 - _TC"}))
# Another rename with multiple dashes
if frappe.db.exists("Warehouse", "Test - Warehouse - Company - _TC"):
frappe.delete_doc("Warehouse", "Test - Warehouse - Company - _TC")
rename_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC", "Test - Warehouse - Company")
self.assertTrue(frappe.db.exists("Account", "Test - Warehouse - Company - _TC"))
self.assertTrue(frappe.db.get_value("Account", filters={"warehouse": "Test - Warehouse - Company - _TC"}))
self.assertFalse(frappe.db.get_value("Account", filters={"warehouse": "Test Warehouse for Renaming 3 - _TC"}))
def test_warehouse_merging(self):
set_perpetual_inventory(1)
@ -96,10 +87,8 @@ class TestWarehouse(unittest.TestCase):
self.assertEqual(bin_qty, existing_bin_qty)
self.assertFalse(frappe.db.exists("Account", "Test Warehouse for Merging 1 - _TC"))
self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Merging 2 - _TC"))
self.assertTrue(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Merging 2 - _TC"}))
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Merging 2 - _TC"}))
def create_warehouse(warehouse_name):
if not frappe.db.exists("Warehouse", warehouse_name + " - _TC"):
@ -107,8 +96,13 @@ def create_warehouse(warehouse_name):
w.warehouse_name = warehouse_name
w.parent_warehouse = "_Test Warehouse Group - _TC"
w.company = "_Test Company"
make_account_for_warehouse(warehouse_name, w)
w.account = warehouse_name + " - _TC"
w.save()
if not frappe.get_value('Account', dict(warehouse=warehouse_name + ' - _TC')):
print 'Warehouse {0} not linked'.format(warehouse_name)
def make_account_for_warehouse(warehouse_name, warehouse_obj):
if not frappe.db.exists("Account", warehouse_name + " - _TC"):
parent_account = frappe.db.get_value('Account',
{'company': warehouse_obj.company, 'is_group':1, 'account_type': 'Stock'},'name')
account = create_account(account_name=warehouse_name, \
account_type="Stock", parent_account= parent_account, company=warehouse_obj.company)

View File

@ -28,7 +28,7 @@ frappe.ui.form.on("Warehouse", {
function() { convert_to_group_or_ledger(frm); }, 'fa fa-retweet', 'btn-default')
}
cur_frm.toggle_enable(['is_group', 'company'], false);
frm.toggle_enable(['is_group', 'company'], false);
frm.fields_dict['parent_warehouse'].get_query = function(doc) {
return {
@ -37,17 +37,18 @@ frappe.ui.form.on("Warehouse", {
}
}
}
}
});
cur_frm.set_query("create_account_under", function() {
return {
filters: {
"company": cur_frm.doc.company,
'is_group': 1
frm.fields_dict['account'].get_query = function(doc) {
return {
filters: {
"is_group": 0,
"account_type": "Stock",
"company": frm.doc.company
}
}
}
}
})
});
function convert_to_group_or_ledger(frm){
frappe.call({

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"beta": 0,
@ -193,9 +194,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:sys_defaults.auto_accounting_for_stock",
"description": "Account for the warehouse (Perpetual Inventory) will be created under this Account.",
"fieldname": "create_account_under",
"fieldname": "account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
@ -204,11 +203,12 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Parent Account",
"label": "Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@ -666,18 +666,18 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-building",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-20 13:27:03.696714",
"modified": "2017-04-21 16:49:19.018576",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",

View File

@ -4,8 +4,9 @@
from __future__ import unicode_literals
import frappe, erpnext
from frappe.utils import cint, validate_email_add
from frappe import throw, msgprint, _
from frappe import throw, _
from frappe.utils.nestedset import NestedSet
from erpnext.stock import get_warehouse_account
class Warehouse(NestedSet):
nsm_parent_field = 'parent_warehouse'
@ -20,8 +21,7 @@ class Warehouse(NestedSet):
def onload(self):
'''load account name for General Ledger Report'''
account = frappe.db.get_value("Account", {
"account_type": "Stock", "company": self.company, "warehouse": self.name, "is_group": 0})
account = self.account or get_warehouse_account(self.name, self.company)
if account:
self.set_onload('account', account)
@ -30,77 +30,9 @@ class Warehouse(NestedSet):
if self.email_id:
validate_email_add(self.email_id, True)
self.update_parent_account()
def update_parent_account(self):
if not getattr(self, "__islocal", None) \
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
and (self.create_account_under != frappe.db.get_value("Warehouse", self.name, "create_account_under")):
self.validate_parent_account()
warehouse_account = frappe.db.get_value("Account",
{"account_type": "Stock", "company": self.company, "warehouse": self.name, "is_group": 0},
["name", "parent_account"])
if warehouse_account and warehouse_account[1] != self.create_account_under:
acc_doc = frappe.get_doc("Account", warehouse_account[0])
acc_doc.parent_account = self.create_account_under
acc_doc.save()
def on_update(self):
self.create_account_head()
self.update_nsm_model()
def create_account_head(self):
'''Create new account head if there is no account linked to this Warehouse'''
from erpnext.accounts.doctype.account.account import BalanceMismatchError
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
if not self.get_account():
if self.get("__islocal") or not frappe.db.get_value(
"Stock Ledger Entry", {"warehouse": self.name}):
self.validate_parent_account()
ac_doc = frappe.get_doc({
"doctype": "Account",
'account_name': self.warehouse_name,
'parent_account': self.parent_warehouse if self.parent_warehouse \
else self.create_account_under,
'is_group': self.is_group,
'company':self.company,
"account_type": "Stock",
"warehouse": self.name,
"freeze_account": "No"
})
ac_doc.flags.ignore_permissions = True
ac_doc.flags.ignore_mandatory = True
try:
ac_doc.insert()
msgprint(_("Account head {0} created").format(ac_doc.name), indicator='green', alert=True)
except frappe.DuplicateEntryError:
msgprint(_("Please create an Account for this Warehouse and link it. This cannot be done automatically as an account with name {0} already exists").format(frappe.bold(self.name)),
indicator='orange')
except BalanceMismatchError:
msgprint(_("Cannot automatically create Account as there is already stock balance in the Account. You must create a matching account before you can make an entry on this warehouse"))
def validate_parent_account(self):
if not self.company:
frappe.throw(_("Warehouse {0}: Company is mandatory").format(self.name))
if not self.create_account_under:
parent_account = frappe.db.sql("""select name from tabAccount
where account_type='Stock' and company=%s and is_group=1
and (warehouse is null or warehouse = '')""", self.company)
if parent_account:
frappe.db.set_value("Warehouse", self.name, "create_account_under", parent_account[0][0])
self.create_account_under = parent_account[0][0]
elif frappe.db.get_value("Account", self.create_account_under, "company") != self.company:
frappe.throw(_("Warehouse {0}: Parent account {1} does not bolong to the company {2}")
.format(self.name, self.create_account_under, self.company))
def update_nsm_model(self):
frappe.utils.nestedset.update_nsm(self)
@ -115,10 +47,6 @@ class Warehouse(NestedSet):
else:
frappe.db.sql("delete from `tabBin` where name = %s", d['name'])
warehouse_account = self.get_account()
if warehouse_account:
frappe.delete_doc("Account", warehouse_account)
if self.check_if_sle_exists():
throw(_("Warehouse can not be deleted as stock ledger entry exists for this warehouse."))
@ -146,36 +74,8 @@ class Warehouse(NestedSet):
if self.company != frappe.db.get_value("Warehouse", new_warehouse, "company"):
frappe.throw(_("Both Warehouse must belong to same Company"))
self.rename_account_for(old_name, new_warehouse, merge)
return new_warehouse
def rename_account_for(self, old_name, new_name, merge):
old_account_name = frappe.get_value('Account', dict(warehouse=old_name))
if old_account_name:
if not merge:
# old account name is same as old name, so rename the account too
if old_account_name == erpnext.encode_company_abbr(old_name, self.company):
frappe.rename_doc("Account", old_account_name, new_name)
else:
# merge
target_account = frappe.get_value('Account', dict(warehouse=new_name))
if target_account:
# target warehouse has account, merge into target account
frappe.rename_doc("Account", old_account_name,
target_account, merge=True)
else:
# target warehouse does not have account, use this account
frappe.rename_doc("Account", old_account_name,
new_name, merge=False)
# rename link
frappe.db.set_value('Account', new_name, 'warehouse', new_name)
def get_account(self):
return frappe.get_value('Account', dict(warehouse=self.name))
def after_rename(self, old_name, new_name, merge=False):
new_warehouse_name = self.get_new_warehouse_name_without_abbr(new_name)
self.db_set("warehouse_name", new_warehouse_name)
@ -222,12 +122,6 @@ class Warehouse(NestedSet):
elif self.check_if_sle_exists():
throw(_("Warehouses with existing transaction can not be converted to ledger."))
else:
account_name = self.get_account()
if account_name:
doc = frappe.get_doc("Account", account_name)
doc.warehouse = self.name
doc.convert_group_to_ledger()
self.is_group = 0
self.save()
return 1
@ -236,12 +130,6 @@ class Warehouse(NestedSet):
if self.check_if_sle_exists():
throw(_("Warehouses with existing transaction can not be converted to group."))
else:
account_name = self.get_account()
if account_name:
doc = frappe.get_doc("Account", account_name)
doc.flags.exclude_account_type_check = True
doc.convert_ledger_to_group()
self.is_group = 1
self.save()
return 1
@ -285,4 +173,4 @@ def add_node():
@frappe.whitelist()
def convert_to_group_or_ledger():
args = frappe.form_dict
return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger()
return frappe.get_doc("Warehouse", args.docname).convert_to_group_or_ledger()