Merge branch 'master' into edge
This commit is contained in:
commit
215fc02395
@ -26,7 +26,6 @@ test_records = [[
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"naming_series": "_T-Journal Voucher-",
|
||||
"posting_date": "2013-02-14",
|
||||
"tds_applicable": "No",
|
||||
"user_remark": "test",
|
||||
"voucher_type": "Bank Voucher",
|
||||
"cheque_no": "33",
|
||||
|
@ -35,7 +35,7 @@ class DocType(BuyingController):
|
||||
self.fname = 'entries'
|
||||
|
||||
def get_credit_to(self):
|
||||
acc_head = sql("select name, credit_days from `tabAccount` where (name = %s or (master_name = %s and master_type = 'supplier')) and docstatus != 2", (cstr(self.doc.supplier) + " - " + self.get_company_abbr(),self.doc.supplier))
|
||||
acc_head = sql("select name, credit_days from `tabAccount` where (name = %s or (master_name = %s and master_type = 'supplier')) and docstatus != 2", (cstr(self.doc.supplier) + " - " + self.company_abbr,self.doc.supplier))
|
||||
|
||||
ret = {}
|
||||
if acc_head and acc_head[0][0]:
|
||||
@ -55,18 +55,30 @@ class DocType(BuyingController):
|
||||
|
||||
return ret
|
||||
|
||||
def get_default_values(self,args):
|
||||
import json
|
||||
args = json.loads(args)
|
||||
ret = {}
|
||||
if sql("select name from `tabItem` where name = '%s'" % args['item_code']):
|
||||
if not args['expense_head'] or args['expense_head'] == 'undefined':
|
||||
expense_head = sql("select name from `tabAccount` where account_name in (select purchase_account from `tabItem` where name = '%s')" % args['item_code'])
|
||||
ret['expense_head'] = expense_head and expense_head[0][0] or ''
|
||||
if not args['cost_center'] or args['cost_center'] == 'undefined':
|
||||
cost_center = sql("select cost_center from `tabItem` where name = '%s'" % args['item_code'])
|
||||
ret['cost_center'] = cost_center and cost_center[0][0] or ''
|
||||
return ret
|
||||
def get_default_values(self, args):
|
||||
if isinstance(args, basestring):
|
||||
import json
|
||||
args = json.loads(args)
|
||||
|
||||
out = webnotes._dict()
|
||||
|
||||
item = webnotes.conn.sql("""select name, purchase_account, cost_center,
|
||||
is_stock_item from `tabItem` where name=%s""", args.get("item_code"), as_dict=1)
|
||||
|
||||
if item and item[0]:
|
||||
item = item[0]
|
||||
|
||||
if cint(webnotes.defaults.get_global_default("auto_inventory_accounting")) and \
|
||||
item.is_stock_item == "Yes":
|
||||
# unset expense head for stock item and auto inventory accounting
|
||||
out.expense_head = out.cost_center = None
|
||||
else:
|
||||
if not args.get("expense_head"):
|
||||
out.expense_head = item.purchase_account
|
||||
if not args.get("cost_center"):
|
||||
out.cost_center = item.cost_center
|
||||
|
||||
return out
|
||||
|
||||
def pull_details(self):
|
||||
if self.doc.purchase_receipt_main:
|
||||
@ -86,9 +98,7 @@ class DocType(BuyingController):
|
||||
def get_expense_account(self, doctype):
|
||||
for d in getlist(self.doclist, doctype):
|
||||
if d.item_code:
|
||||
item = webnotes.conn.sql("select purchase_account, cost_center from tabItem where name = '%s'" %(d.item_code), as_dict=1)
|
||||
d.expense_head = item and item[0]['purchase_account'] or ''
|
||||
d.cost_center = item and item[0]['cost_center'] or ''
|
||||
d.fields.update(self.get_default_values(d.fields))
|
||||
|
||||
def get_advances(self):
|
||||
super(DocType, self).get_advances(self.doc.credit_to,
|
||||
@ -110,9 +120,6 @@ class DocType(BuyingController):
|
||||
ret={'add_tax_rate' :rate and flt(rate[0][0]) or 0 }
|
||||
return ret
|
||||
|
||||
def get_company_abbr(self):
|
||||
return sql("select abbr from tabCompany where name=%s", self.doc.company)[0][0]
|
||||
|
||||
def validate_duplicate_docname(self,doctype):
|
||||
for d in getlist(self.doclist, 'entries'):
|
||||
if doctype == 'purchase_receipt' and cstr(self.doc.purchase_receipt_main) == cstr(d.purchase_receipt):
|
||||
@ -186,7 +193,7 @@ class DocType(BuyingController):
|
||||
if self.doc.supplier and self.doc.credit_to:
|
||||
acc_head = sql("select master_name from `tabAccount` where name = %s", self.doc.credit_to)
|
||||
|
||||
if (acc_head and cstr(acc_head[0][0]) != cstr(self.doc.supplier)) or (not acc_head and (self.doc.credit_to != cstr(self.doc.supplier) + " - " + self.get_company_abbr())):
|
||||
if (acc_head and cstr(acc_head[0][0]) != cstr(self.doc.supplier)) or (not acc_head and (self.doc.credit_to != cstr(self.doc.supplier) + " - " + self.company_abbr)):
|
||||
msgprint("Credit To: %s do not match with Supplier: %s for Company: %s.\n If both correctly entered, please select Master Type and Master Name in account master." %(self.doc.credit_to,self.doc.supplier,self.doc.company), raise_exception=1)
|
||||
|
||||
# Check for Stopped PO
|
||||
@ -256,11 +263,25 @@ class DocType(BuyingController):
|
||||
raise Exception
|
||||
|
||||
def set_against_expense_account(self):
|
||||
against_acc = []
|
||||
for d in getlist(self.doclist, 'entries'):
|
||||
if d.expense_account not in against_acc:
|
||||
against_acc.append(d.expense_account)
|
||||
self.doc.against_expense_account = ','.join(against_acc)
|
||||
auto_inventory_accounting = \
|
||||
cint(webnotes.defaults.get_global_default("auto_inventory_accounting"))
|
||||
stock_not_billed_account = "Stock Received But Not Billed - %s" % self.company_abbr
|
||||
|
||||
against_accounts = []
|
||||
for item in self.doclist.get({"parentfield": "entries"}):
|
||||
if auto_inventory_accounting and item.item_code in self.stock_items:
|
||||
# in case of auto inventory accounting, against expense account is always
|
||||
# Stock Received But Not Billed for a stock item
|
||||
item.expense_head = item.cost_center = None
|
||||
|
||||
if stock_not_billed_account not in against_accounts:
|
||||
against_accounts.append(stock_not_billed_account)
|
||||
|
||||
elif item.expense_head not in against_accounts:
|
||||
# if no auto_inventory_accounting or not a stock item
|
||||
against_accounts.append(item.expense_head)
|
||||
|
||||
self.doc.against_expense_account = ",".join(against_accounts)
|
||||
|
||||
def po_required(self):
|
||||
res = sql("select value from `tabSingles` where doctype = 'Global Defaults' and field = 'po_required'")
|
||||
@ -320,6 +341,8 @@ class DocType(BuyingController):
|
||||
self.doc.posting_date,'Posting Date')
|
||||
|
||||
self.validate_write_off_account()
|
||||
self.update_raw_material_cost()
|
||||
self.update_valuation_rate("entries")
|
||||
|
||||
def check_prev_docstatus(self):
|
||||
for d in getlist(self.doclist,'entries'):
|
||||
@ -382,11 +405,10 @@ class DocType(BuyingController):
|
||||
|
||||
def make_gl_entries(self, is_cancel = 0):
|
||||
from accounts.general_ledger import make_gl_entries
|
||||
auto_inventory_accounting = \
|
||||
cint(webnotes.defaults.get_global_default("auto_inventory_accounting"))
|
||||
|
||||
gl_entries = []
|
||||
valuation_tax = 0
|
||||
auto_inventory_accounting = webnotes.conn.get_value("Global Defaults", None,
|
||||
"auto_inventory_accounting")
|
||||
abbr = self.get_company_abbr()
|
||||
|
||||
# parent's gl entry
|
||||
if self.doc.grand_total:
|
||||
@ -402,11 +424,9 @@ class DocType(BuyingController):
|
||||
)
|
||||
|
||||
# tax table gl entries
|
||||
for tax in getlist(self.doclist, "purchase_tax_details"):
|
||||
valuation_tax = 0
|
||||
for tax in self.doclist.get({"parentfield": "purchase_tax_details"}):
|
||||
if tax.category in ("Total", "Valuation and Total") and flt(tax.tax_amount):
|
||||
valuation_tax += tax.add_deduct_tax == "Add" \
|
||||
and flt(tax.tax_amount) or -1 * flt(tax.tax_amount)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": tax.account_head,
|
||||
@ -418,24 +438,30 @@ class DocType(BuyingController):
|
||||
}, is_cancel)
|
||||
)
|
||||
|
||||
# item gl entries
|
||||
stock_item_and_auto_accounting = False
|
||||
for item in self.doclist.get({"parentfield": "entries"}):
|
||||
if auto_inventory_accounting and flt(item.valuation_rate) and \
|
||||
webnotes.conn.get_value("Item", item.item_code, "is_stock_item")=="Yes":
|
||||
# if auto inventory accounting enabled and stock item,
|
||||
# then do stock related gl entries, expense will be booked in sales invoice
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": "Stock Received But Not Billed - %s" % (abbr,),
|
||||
"against": self.doc.credit_to,
|
||||
"debit": flt(item.valuation_rate) * flt(item.conversion_factor) \
|
||||
* item.qty,
|
||||
"remarks": self.doc.remarks or "Accounting Entry for Stock"
|
||||
}, is_cancel)
|
||||
)
|
||||
# accumulate valuation tax
|
||||
if tax.category in ("Valuation", "Valuation and Total") and flt(tax.tax_amount):
|
||||
valuation_tax += (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.tax_amount)
|
||||
|
||||
stock_item_and_auto_accounting = True
|
||||
# item gl entries
|
||||
stock_item_and_auto_inventory_accounting = False
|
||||
for item in self.doclist.get({"parentfield": "entries"}):
|
||||
if auto_inventory_accounting and item.item_code in self.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_inventory_accounting = True
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": "Stock Received But Not Billed - %s" % (self.company_abbr,),
|
||||
"against": self.doc.credit_to,
|
||||
"debit": flt(item.valuation_rate) * flt(item.conversion_factor) \
|
||||
* flt(item.qty),
|
||||
"remarks": self.doc.remarks or "Accounting Entry for Stock"
|
||||
}, is_cancel)
|
||||
)
|
||||
|
||||
elif flt(item.amount):
|
||||
# if not a stock item or auto inventory accounting disabled, book the expense
|
||||
@ -449,13 +475,13 @@ class DocType(BuyingController):
|
||||
}, is_cancel)
|
||||
)
|
||||
|
||||
if stock_item_and_auto_accounting and valuation_tax:
|
||||
if stock_item_and_auto_inventory_accounting and valuation_tax:
|
||||
# credit valuation tax amount in "Expenses Included In Valuation"
|
||||
# this will balance out valuation amount included in cost of goods sold
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": "Expenses Included In Valuation - %s" % (abbr,),
|
||||
"cost_center": "ERP - %s" % abbr, # to-do
|
||||
"account": "Expenses Included In Valuation - %s" % (self.company_abbr,),
|
||||
"cost_center": "Auto Inventory Accounting - %s" % self.company_abbr,
|
||||
"against": self.doc.credit_to,
|
||||
"credit": valuation_tax,
|
||||
"remarks": self.doc.remarks or "Accounting Entry for Stock"
|
||||
@ -464,12 +490,12 @@ class DocType(BuyingController):
|
||||
|
||||
# writeoff account includes petty difference in the invoice amount
|
||||
# and the amount that is paid
|
||||
if self.doc.write_off_account and self.doc.write_off_amount:
|
||||
if self.doc.write_off_account and flt(self.doc.write_off_amount):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.doc.write_off_account,
|
||||
"against": self.doc.credit_to,
|
||||
"credit": self.doc.write_off_amount,
|
||||
"credit": flt(self.doc.write_off_amount),
|
||||
"remarks": self.doc.remarks,
|
||||
"cost_center": self.doc.write_off_cost_center
|
||||
}, is_cancel)
|
||||
@ -478,7 +504,6 @@ class DocType(BuyingController):
|
||||
if gl_entries:
|
||||
make_gl_entries(gl_entries, cancel=is_cancel)
|
||||
|
||||
|
||||
def check_next_docstatus(self):
|
||||
submit_jv = sql("select t1.name from `tabJournal Voucher` t1,`tabJournal Voucher Detail` t2 where t1.name = t2.parent and t2.against_voucher = '%s' and t1.docstatus = 1" % (self.doc.name))
|
||||
if submit_jv:
|
||||
@ -493,4 +518,16 @@ class DocType(BuyingController):
|
||||
|
||||
def on_update(self):
|
||||
pass
|
||||
def update_raw_material_cost(self):
|
||||
if self.sub_contracted_items:
|
||||
for d in self.doclist.get({"parentfield": "entries"}):
|
||||
rm_cost = webnotes.conn.sql(""" select raw_material_cost / quantity
|
||||
from `tabBOM` where item = %s and is_default = 1 and docstatus = 1
|
||||
and is_active = 1 """, (d.item_code,))
|
||||
rm_cost = rm_cost and flt(rm_cost[0][0]) or 0
|
||||
|
||||
d.conversion_factor = d.conversion_factor or webnotes.conn.get_value(
|
||||
"UOM Conversion Detail", {"parent": d.item_code, "uom": d.uom},
|
||||
"conversion_factor") or 1
|
||||
|
||||
d.rm_supp_cost = rm_cost * flt(d.qty) * flt(d.conversion_factor)
|
||||
|
@ -20,18 +20,18 @@ import unittest
|
||||
import webnotes
|
||||
import webnotes.model
|
||||
import json
|
||||
from webnotes.utils import cint
|
||||
import webnotes.defaults
|
||||
|
||||
test_dependencies = ["Item", "Cost Center"]
|
||||
|
||||
class TestPurchaseInvoice(unittest.TestCase):
|
||||
def test_gl_entries(self):
|
||||
wrapper = webnotes.bean(self.get_test_doclist())
|
||||
|
||||
# circumvent the disabled calculation call
|
||||
obj = webnotes.get_obj(doc=wrapper.doc, doclist=wrapper.doclist)
|
||||
obj.calculate_taxes_and_totals()
|
||||
wrapper.set_doclist(obj.doclist)
|
||||
def test_gl_entries_without_auto_inventory_accounting(self):
|
||||
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
|
||||
self.assertTrue(not cint(webnotes.defaults.get_global_default("auto_inventory_accounting")))
|
||||
|
||||
wrapper = webnotes.bean(copy=test_records[0])
|
||||
wrapper.run_method("calculate_taxes_and_totals")
|
||||
wrapper.insert()
|
||||
wrapper.submit()
|
||||
wrapper.load_from_db()
|
||||
@ -53,14 +53,38 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
for d in gl_entries:
|
||||
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
|
||||
|
||||
def test_gl_entries_with_auto_inventory_accounting(self):
|
||||
webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
|
||||
self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 1)
|
||||
|
||||
pi = webnotes.bean(copy=test_records[1])
|
||||
pi.run_method("calculate_taxes_and_totals")
|
||||
pi.insert()
|
||||
pi.submit()
|
||||
|
||||
gl_entries = webnotes.conn.sql("""select account, debit, credit
|
||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||
order by account asc""", pi.doc.name, as_dict=1)
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
expected_values = sorted([
|
||||
["_Test Supplier - _TC", 0, 720],
|
||||
["Stock Received But Not Billed - _TC", 750.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):
|
||||
self.assertEquals(expected_values[i][0], gle.account)
|
||||
self.assertEquals(expected_values[i][1], gle.debit)
|
||||
self.assertEquals(expected_values[i][2], gle.credit)
|
||||
|
||||
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
|
||||
|
||||
def test_purchase_invoice_calculation(self):
|
||||
wrapper = webnotes.bean(self.get_test_doclist())
|
||||
|
||||
# circumvent the disabled calculation call
|
||||
obj = webnotes.get_obj(doc=wrapper.doc, doclist=wrapper.doclist)
|
||||
obj.calculate_taxes_and_totals()
|
||||
wrapper.set_doclist(obj.doclist)
|
||||
|
||||
wrapper = webnotes.bean(copy=test_records[0])
|
||||
wrapper.run_method("calculate_taxes_and_totals")
|
||||
wrapper.insert()
|
||||
wrapper.load_from_db()
|
||||
|
||||
@ -92,144 +116,209 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertEqual(item.item_code, expected_values[i][0])
|
||||
self.assertEqual(item.item_tax_amount, expected_values[i][1])
|
||||
|
||||
def get_test_doclist(self):
|
||||
return [
|
||||
# parent
|
||||
{
|
||||
"doctype": "Purchase Invoice",
|
||||
"naming_series": "BILL",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"credit_to": "_Test Supplier - _TC",
|
||||
"bill_no": "NA",
|
||||
"posting_date": "2013-02-03",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"company": "_Test Company",
|
||||
"currency": "INR",
|
||||
"conversion_rate": 1,
|
||||
"grand_total_import": 0 # for feed
|
||||
},
|
||||
# items
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"item_name": "_Test Item Home Desktop 100",
|
||||
"qty": 10,
|
||||
"import_rate": 50,
|
||||
"import_amount": 500,
|
||||
"rate": 50,
|
||||
"amount": 500,
|
||||
"uom": "_Test UOM",
|
||||
"item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}),
|
||||
"expense_head": "_Test Account Cost for Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
test_records = [
|
||||
[
|
||||
# parent
|
||||
{
|
||||
"doctype": "Purchase Invoice",
|
||||
"naming_series": "BILL",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"credit_to": "_Test Supplier - _TC",
|
||||
"bill_no": "NA",
|
||||
"posting_date": "2013-02-03",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"company": "_Test Company",
|
||||
"currency": "INR",
|
||||
"conversion_rate": 1,
|
||||
"grand_total_import": 0 # for feed
|
||||
},
|
||||
# items
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"item_name": "_Test Item Home Desktop 100",
|
||||
"qty": 10,
|
||||
"import_rate": 50,
|
||||
"import_amount": 500,
|
||||
"rate": 50,
|
||||
"amount": 500,
|
||||
"uom": "_Test UOM",
|
||||
"item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}),
|
||||
"expense_head": "_Test Account Cost for Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"conversion_factor": 1.0,
|
||||
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item Home Desktop 200",
|
||||
"item_name": "_Test Item Home Desktop 200",
|
||||
"qty": 5,
|
||||
"import_rate": 150,
|
||||
"import_amount": 750,
|
||||
"rate": 150,
|
||||
"amount": 750,
|
||||
"uom": "_Test UOM",
|
||||
"expense_head": "_Test Account Cost for Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC"
|
||||
},
|
||||
# taxes
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Shipping Charges",
|
||||
"category": "Valuation and Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 100
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Customs Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Customs Duty",
|
||||
"category": "Valuation",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 10
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Excise Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Excise Duty",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 12
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Previous Row Amount",
|
||||
"account_head": "_Test Account Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Education Cess",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 2,
|
||||
"row_id": 3
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Previous Row Amount",
|
||||
"account_head": "_Test Account S&H Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "S&H Education Cess",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 1,
|
||||
"row_id": 3
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Previous Row Total",
|
||||
"account_head": "_Test Account CST - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "CST",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 2,
|
||||
"row_id": 5
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "VAT",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 12.5
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Previous Row Total",
|
||||
"account_head": "_Test Account Discount - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Discount",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Deduct",
|
||||
"rate": 10,
|
||||
"row_id": 7
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item Home Desktop 200",
|
||||
"item_name": "_Test Item Home Desktop 200",
|
||||
"qty": 5,
|
||||
"import_rate": 150,
|
||||
"import_amount": 750,
|
||||
"rate": 150,
|
||||
"amount": 750,
|
||||
"uom": "_Test UOM",
|
||||
"expense_head": "_Test Account Cost for Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"conversion_factor": 1.0,
|
||||
},
|
||||
# taxes
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Shipping Charges",
|
||||
"category": "Valuation and Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 100
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Customs Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Customs Duty",
|
||||
"category": "Valuation",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 10
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Excise Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Excise Duty",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 12
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Previous Row Amount",
|
||||
"account_head": "_Test Account Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Education Cess",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 2,
|
||||
"row_id": 3
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Previous Row Amount",
|
||||
"account_head": "_Test Account S&H Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "S&H Education Cess",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 1,
|
||||
"row_id": 3
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Previous Row Total",
|
||||
"account_head": "_Test Account CST - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "CST",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 2,
|
||||
"row_id": 5
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "VAT",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 12.5
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "On Previous Row Total",
|
||||
"account_head": "_Test Account Discount - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Discount",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Deduct",
|
||||
"rate": 10,
|
||||
"row_id": 7
|
||||
},
|
||||
],
|
||||
[
|
||||
# parent
|
||||
{
|
||||
"doctype": "Purchase Invoice",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"credit_to": "_Test Supplier - _TC",
|
||||
"bill_no": "NA",
|
||||
"posting_date": "2013-02-03",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"company": "_Test Company",
|
||||
"currency": "INR",
|
||||
"conversion_rate": 1.0,
|
||||
"grand_total_import": 0 # for feed
|
||||
},
|
||||
# items
|
||||
{
|
||||
"doctype": "Purchase Invoice Item",
|
||||
"parentfield": "entries",
|
||||
"item_code": "_Test Item",
|
||||
"item_name": "_Test Item",
|
||||
"qty": 10.0,
|
||||
"import_rate": 50.0,
|
||||
"uom": "_Test UOM",
|
||||
"expense_head": "_Test Account Cost for Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"conversion_factor": 1.0,
|
||||
},
|
||||
# taxes
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account Shipping Charges - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Shipping Charges",
|
||||
"category": "Valuation and Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 100.0
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "VAT",
|
||||
"category": "Total",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 120.0
|
||||
},
|
||||
{
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"parentfield": "purchase_tax_details",
|
||||
"charge_type": "Actual",
|
||||
"account_head": "_Test Account Customs Duty - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Customs Duty",
|
||||
"category": "Valuation",
|
||||
"add_deduct_tax": "Add",
|
||||
"rate": 150.0
|
||||
},
|
||||
]
|
||||
]
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-01-29 20:53:00",
|
||||
"creation": "2013-02-11 10:54:51",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-02-08 14:06:17",
|
||||
"modified": "2013-02-27 18:10:04",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -282,6 +282,35 @@
|
||||
"search_index": 0,
|
||||
"width": "150px"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "valuation_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Valuation Rate",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "conversion_factor",
|
||||
"fieldtype": "Float",
|
||||
"label": "Conversion Factor",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "rm_supp_cost",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Raw Materials Supplied Cost",
|
||||
"no_copy": 1,
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"doctype": "DocField",
|
||||
|
@ -87,3 +87,10 @@ class AccountsController(TransactionBase):
|
||||
(", ".join((["%s"]*len(item_codes))),), item_codes)]
|
||||
|
||||
return self._stock_items
|
||||
|
||||
@property
|
||||
def company_abbr(self):
|
||||
if not hasattr(self, "_abbr"):
|
||||
self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr")
|
||||
|
||||
return self._abbr
|
@ -232,7 +232,7 @@ class BuyingController(AccountsController):
|
||||
else:
|
||||
self.doc.grand_total = flt(self.doc.net_total,
|
||||
self.precision.main.grand_total)
|
||||
self.doc.grand_total_print = flt(
|
||||
self.doc.grand_total_import = flt(
|
||||
self.doc.grand_total / self.doc.conversion_rate,
|
||||
self.precision.main.grand_total_import)
|
||||
|
||||
@ -250,7 +250,9 @@ class BuyingController(AccountsController):
|
||||
if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus == 0:
|
||||
self.doc.total_advance = flt(self.doc.total_advance,
|
||||
self.precision.main.total_advance)
|
||||
self.doc.outstanding_amount = flt(self.doc.grand_total - self.doc.total_advance,
|
||||
self.doc.total_amount_to_pay = flt(self.doc.grand_total - flt(self.doc.write_off_amount,
|
||||
self.precision.main.write_off_amount), self.precision.main.total_amount_to_pay)
|
||||
self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
|
||||
self.precision.main.outstanding_amount)
|
||||
|
||||
def _cleanup(self):
|
||||
@ -326,6 +328,16 @@ class BuyingController(AccountsController):
|
||||
item.item_tax_amount += flt(current_tax_amount,
|
||||
self.precision.item.item_tax_amount)
|
||||
|
||||
# update valuation rate
|
||||
def update_valuation_rate(self, parentfield):
|
||||
for d in self.doclist.get({"parentfield": parentfield}):
|
||||
if d.qty:
|
||||
d.valuation_rate = (flt(d.purchase_rate or d.rate)
|
||||
+ (flt(d.item_tax_amount) + flt(d.rm_supp_cost)) / flt(d.qty)
|
||||
) / flt(d.conversion_factor)
|
||||
else:
|
||||
d.valuation_rate = 0.0
|
||||
|
||||
@property
|
||||
def precision(self):
|
||||
if not hasattr(self, "_precision"):
|
||||
@ -336,3 +348,25 @@ class BuyingController(AccountsController):
|
||||
self._precision.tax = self.meta.get_precision_map(parentfield = \
|
||||
"purchase_tax_details")
|
||||
return self._precision
|
||||
|
||||
@property
|
||||
def sub_contracted_items(self):
|
||||
if not hasattr(self, "_sub_contracted_items"):
|
||||
item_codes = list(set(item.item_code for item in
|
||||
self.doclist.get({"parentfield": self.fname})))
|
||||
self._sub_contracted_items = [r[0] for r in webnotes.conn.sql("""select name
|
||||
from `tabItem` where name in (%s) and is_sub_contracted_item='Yes'""" % \
|
||||
(", ".join((["%s"]*len(item_codes))),), item_codes)]
|
||||
|
||||
return self._sub_contracted_items
|
||||
|
||||
@property
|
||||
def purchase_items(self):
|
||||
if not hasattr(self, "_purchase_items"):
|
||||
item_codes = list(set(item.item_code for item in
|
||||
self.doclist.get({"parentfield": self.fname})))
|
||||
self._purchase_items = [r[0] for r in webnotes.conn.sql("""select name
|
||||
from `tabItem` where name in (%s) and is_purchase_item='Yes'""" % \
|
||||
(", ".join((["%s"]*len(item_codes))),), item_codes)]
|
||||
|
||||
return self._purchase_items
|
||||
|
@ -1,8 +1,14 @@
|
||||
erpnext.updates = [
|
||||
["27th February", [
|
||||
"Time Log: Created Time Log System, with Calendar View."
|
||||
]],
|
||||
["26th February", [
|
||||
"Gross Profit: The report has been rewritten and now it is under Accounts module"
|
||||
]
|
||||
],
|
||||
]],
|
||||
["25th February", [
|
||||
"Employee Leave Balance: New Report",
|
||||
"Scripted Reports: Ability to create reports via Script",
|
||||
]],
|
||||
["21st February, 2013", [
|
||||
"Item: Warehouse-wise Re-order Level and Quantity",
|
||||
"Buying: Purchase Request renamed to Material Request"
|
||||
|
@ -103,8 +103,10 @@ class DocType:
|
||||
def get_carry_forwarded_leaves(self):
|
||||
if self.doc.carry_forward:
|
||||
self.allow_carry_forward()
|
||||
prev_fiscal_year = sql("""select name from `tabFiscal Year`
|
||||
where name < %s order by name desc limit 1""", self.doc.fiscal_year)
|
||||
prev_fiscal_year = webnotes.conn.sql("""select name from `tabFiscal Year`
|
||||
where year_start_date = (select date_add(year_start_date, interval -1 year)
|
||||
from `tabFiscal Year` where name=%s)
|
||||
order by name desc limit 1""", self.doc.fiscal_year)
|
||||
prev_fiscal_year = prev_fiscal_year and prev_fiscal_year[0][0] or ''
|
||||
prev_bal = 0
|
||||
if prev_fiscal_year and cint(self.doc.carry_forward) == 1:
|
||||
|
@ -236,13 +236,10 @@ def get_events(start, end):
|
||||
match_conditions = build_match_conditions("Leave Application")
|
||||
|
||||
# show department leaves for employee
|
||||
show_department_leaves = match_conditions and \
|
||||
len(match_conditions.split("or"))==1 and "employee" in match_conditions
|
||||
|
||||
if show_department_leaves:
|
||||
if "Employee" in webnotes.get_roles():
|
||||
add_department_leaves(events, start, end, employee, company)
|
||||
else:
|
||||
add_leaves(events, start, end, employee, company, match_conditions)
|
||||
|
||||
add_leaves(events, start, end, employee, company, match_conditions)
|
||||
|
||||
add_block_dates(events, start, end, employee, company)
|
||||
add_holidays(events, start, end, employee, company)
|
||||
@ -273,7 +270,7 @@ def add_leaves(events, start, end, employee, company, match_conditions=None):
|
||||
query += " and " + match_conditions
|
||||
|
||||
for d in webnotes.conn.sql(query, (start, end, start, end), as_dict=True):
|
||||
events.append({
|
||||
e = {
|
||||
"name": d.name,
|
||||
"doctype": "Leave Application",
|
||||
"from_date": d.from_date,
|
||||
@ -282,7 +279,9 @@ def add_leaves(events, start, end, employee, company, match_conditions=None):
|
||||
"title": _("Leave by") + " " + cstr(d.employee_name) + \
|
||||
(d.half_day and _(" (Half Day)") or ""),
|
||||
"docstatus": d.docstatus
|
||||
})
|
||||
}
|
||||
if e not in events:
|
||||
events.append(e)
|
||||
|
||||
def add_block_dates(events, start, end, employee, company):
|
||||
# block days
|
||||
|
@ -6,6 +6,13 @@ wn.query_reports["Employee Leave Balance"] = {
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": wn.defaults.get_user_default("fiscal_year")
|
||||
},
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": "Company",
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"default": wn.defaults.get_user_default("company")
|
||||
}
|
||||
]
|
||||
}
|
@ -4,7 +4,11 @@ from webnotes.widgets.reportview import execute as runreport
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
employees = runreport(doctype="Employee", fields=["name", "employee_name", "department"])
|
||||
|
||||
employee_filters = filters.get("company") and \
|
||||
[["Employee", "company", "=", filters.get("company")]] or None
|
||||
employees = runreport(doctype="Employee", fields=["name", "employee_name", "department"],
|
||||
filters=employee_filters)
|
||||
leave_types = webnotes.conn.sql_list("select name from `tabLeave Type`")
|
||||
|
||||
if filters.get("fiscal_year"):
|
||||
@ -14,8 +18,6 @@ def execute(filters=None):
|
||||
|
||||
employee_in = '", "'.join([e.name for e in employees])
|
||||
|
||||
|
||||
|
||||
allocations = webnotes.conn.sql("""select employee, fiscal_year, leave_type, total_leaves_allocated
|
||||
from `tabLeave Allocation`
|
||||
where docstatus=1 and employee in ("%s")""" % employee_in, as_dict=True)
|
||||
|
@ -18,130 +18,164 @@
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import webnotes
|
||||
import webnotes.model
|
||||
from webnotes.utils import nowdate, flt
|
||||
from accounts.utils import get_fiscal_year
|
||||
from webnotes.model.doclist import DocList
|
||||
import copy
|
||||
|
||||
company = webnotes.conn.get_default("company")
|
||||
|
||||
|
||||
def load_data():
|
||||
|
||||
# create default warehouse
|
||||
if not webnotes.conn.exists("Warehouse", "Default Warehouse"):
|
||||
webnotes.insert({"doctype": "Warehouse",
|
||||
"warehouse_name": "Default Warehouse",
|
||||
"warehouse_type": "Stores"})
|
||||
|
||||
# create UOM: Nos.
|
||||
if not webnotes.conn.exists("UOM", "Nos"):
|
||||
webnotes.insert({"doctype": "UOM", "uom_name": "Nos"})
|
||||
|
||||
from webnotes.tests import insert_test_data
|
||||
# create item groups and items
|
||||
insert_test_data("Item Group",
|
||||
sort_fn=lambda ig: (ig[0].get('parent_item_group'), ig[0].get('name')))
|
||||
insert_test_data("Item")
|
||||
|
||||
base_bom_fg = [
|
||||
{"doctype": "BOM", "item": "Android Jack D", "quantity": 1,
|
||||
"is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
{"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
"opn_description": "Development", "hour_rate": 10, "time_in_mins": 90},
|
||||
{"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
"qty": 2, "rate": 20, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
{"doctype": "BOM Item", "item_code": "Home Desktop 100", "operation_no": 1,
|
||||
"qty": 1, "rate": 300, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
{"doctype": "BOM Item", "item_code": "Nebula 7", "operation_no": 1,
|
||||
"qty": 5, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
]
|
||||
|
||||
base_bom_child = [
|
||||
{"doctype": "BOM", "item": "Nebula 7", "quantity": 5,
|
||||
"is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
{"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
"opn_description": "Development"},
|
||||
{"doctype": "BOM Item", "item_code": "Android Jack S", "operation_no": 1,
|
||||
"qty": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
]
|
||||
|
||||
base_bom_grandchild = [
|
||||
{"doctype": "BOM", "item": "Android Jack S", "quantity": 1,
|
||||
"is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
{"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
"opn_description": "Development"},
|
||||
{"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
"qty": 3, "rate": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
]
|
||||
|
||||
|
||||
class TestPurchaseReceipt(unittest.TestCase):
|
||||
def setUp(self):
|
||||
webnotes.conn.begin()
|
||||
load_data()
|
||||
|
||||
def test_bom_validation(self):
|
||||
# show throw error bacause bom no missing for sub-assembly item
|
||||
bom_fg = copy.deepcopy(base_bom_fg)
|
||||
self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
|
||||
# main item is not a manufacturing item
|
||||
bom_fg = copy.deepcopy(base_bom_fg)
|
||||
bom_fg[0]["item"] = "Home Desktop 200"
|
||||
bom_fg.pop(4)
|
||||
self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
|
||||
# operation no mentioed in material table not matching with operation table
|
||||
bom_fg = copy.deepcopy(base_bom_fg)
|
||||
bom_fg.pop(4)
|
||||
bom_fg[2]["operation_no"] = 2
|
||||
self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
|
||||
|
||||
def test_bom(self):
|
||||
gc_wrapper = webnotes.insert(DocList(base_bom_grandchild))
|
||||
gc_wrapper.submit()
|
||||
|
||||
bom_child = copy.deepcopy(base_bom_child)
|
||||
bom_child[2]["bom_no"] = gc_wrapper.doc.name
|
||||
child_wrapper = webnotes.insert(DocList(bom_child))
|
||||
child_wrapper.submit()
|
||||
|
||||
bom_fg = copy.deepcopy(base_bom_fg)
|
||||
bom_fg[4]["bom_no"] = child_wrapper.doc.name
|
||||
fg_wrapper = webnotes.insert(DocList(bom_fg))
|
||||
fg_wrapper.load_from_db()
|
||||
|
||||
self.check_bom_cost(fg_wrapper)
|
||||
|
||||
self.check_flat_bom(fg_wrapper, child_wrapper, gc_wrapper)
|
||||
|
||||
def check_bom_cost(self, fg_wrapper):
|
||||
expected_values = {
|
||||
"operating_cost": 15,
|
||||
"raw_material_cost": 640,
|
||||
"total_cost": 655
|
||||
test_records = [
|
||||
[
|
||||
{
|
||||
"doctype": "BOM",
|
||||
"item": "_Test FG Item",
|
||||
"quantity": 1.0,
|
||||
"is_active": 1,
|
||||
"is_default": 1,
|
||||
"docstatus": 1
|
||||
},
|
||||
{
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item",
|
||||
"parentfield": "bom_materials",
|
||||
"qty": 1.0,
|
||||
"rate": 5000.0,
|
||||
"amount": 5000.0,
|
||||
"stock_uom": "No."
|
||||
},
|
||||
{
|
||||
"doctype": "BOM Item",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"parentfield": "bom_materials",
|
||||
"qty": 2.0,
|
||||
"rate": 1000.0,
|
||||
"amount": 2000.0,
|
||||
"stock_uom": "No."
|
||||
}
|
||||
|
||||
for key in expected_values:
|
||||
self.assertEqual(flt(expected_values[key]), flt(fg_wrapper.doc.fields.get(key)))
|
||||
|
||||
def check_flat_bom(self, fg_wrapper, child_wrapper, gc_wrapper):
|
||||
expected_flat_bom_items = {
|
||||
("Home Desktop 300", fg_wrapper.doc.name): (2, 20),
|
||||
("Home Desktop 100", fg_wrapper.doc.name): (1, 300),
|
||||
("Home Desktop 300", gc_wrapper.doc.name): (30, 10)
|
||||
}
|
||||
|
||||
self.assertEqual(len(fg_wrapper.doclist.get({"parentfield": "flat_bom_details"})), 3)
|
||||
|
||||
for key, val in expected_flat_bom_items.items():
|
||||
flat_bom = fg_wrapper.doclist.get({"parentfield": "flat_bom_details",
|
||||
"item_code": key[0], "parent_bom": key[1]})[0]
|
||||
self.assertEqual(val, (flat_bom.qty, flat_bom.rate))
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
webnotes.conn.rollback()
|
||||
|
||||
# import webnotes.model
|
||||
# from webnotes.utils import nowdate, flt
|
||||
# from accounts.utils import get_fiscal_year
|
||||
# from webnotes.model.doclist import DocList
|
||||
# import copy
|
||||
#
|
||||
# company = webnotes.conn.get_default("company")
|
||||
#
|
||||
#
|
||||
# def load_data():
|
||||
#
|
||||
# # create default warehouse
|
||||
# if not webnotes.conn.exists("Warehouse", "Default Warehouse"):
|
||||
# webnotes.insert({"doctype": "Warehouse",
|
||||
# "warehouse_name": "Default Warehouse",
|
||||
# "warehouse_type": "Stores"})
|
||||
#
|
||||
# # create UOM: Nos.
|
||||
# if not webnotes.conn.exists("UOM", "Nos"):
|
||||
# webnotes.insert({"doctype": "UOM", "uom_name": "Nos"})
|
||||
#
|
||||
# from webnotes.tests import insert_test_data
|
||||
# # create item groups and items
|
||||
# insert_test_data("Item Group",
|
||||
# sort_fn=lambda ig: (ig[0].get('parent_item_group'), ig[0].get('name')))
|
||||
# insert_test_data("Item")
|
||||
#
|
||||
# base_bom_fg = [
|
||||
# {"doctype": "BOM", "item": "Android Jack D", "quantity": 1,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development", "hour_rate": 10, "time_in_mins": 90},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
# "qty": 2, "rate": 20, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 100", "operation_no": 1,
|
||||
# "qty": 1, "rate": 300, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# {"doctype": "BOM Item", "item_code": "Nebula 7", "operation_no": 1,
|
||||
# "qty": 5, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# ]
|
||||
#
|
||||
# base_bom_child = [
|
||||
# {"doctype": "BOM", "item": "Nebula 7", "quantity": 5,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development"},
|
||||
# {"doctype": "BOM Item", "item_code": "Android Jack S", "operation_no": 1,
|
||||
# "qty": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
# ]
|
||||
#
|
||||
# base_bom_grandchild = [
|
||||
# {"doctype": "BOM", "item": "Android Jack S", "quantity": 1,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development"},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
# "qty": 3, "rate": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
# ]
|
||||
#
|
||||
#
|
||||
# class TestPurchaseReceipt(unittest.TestCase):
|
||||
# def setUp(self):
|
||||
# webnotes.conn.begin()
|
||||
# load_data()
|
||||
#
|
||||
# def test_bom_validation(self):
|
||||
# # show throw error bacause bom no missing for sub-assembly item
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
# # main item is not a manufacturing item
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg[0]["item"] = "Home Desktop 200"
|
||||
# bom_fg.pop(4)
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
# # operation no mentioed in material table not matching with operation table
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg.pop(4)
|
||||
# bom_fg[2]["operation_no"] = 2
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
#
|
||||
# def test_bom(self):
|
||||
# gc_wrapper = webnotes.insert(DocList(base_bom_grandchild))
|
||||
# gc_wrapper.submit()
|
||||
#
|
||||
# bom_child = copy.deepcopy(base_bom_child)
|
||||
# bom_child[2]["bom_no"] = gc_wrapper.doc.name
|
||||
# child_wrapper = webnotes.insert(DocList(bom_child))
|
||||
# child_wrapper.submit()
|
||||
#
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg[4]["bom_no"] = child_wrapper.doc.name
|
||||
# fg_wrapper = webnotes.insert(DocList(bom_fg))
|
||||
# fg_wrapper.load_from_db()
|
||||
#
|
||||
# self.check_bom_cost(fg_wrapper)
|
||||
#
|
||||
# self.check_flat_bom(fg_wrapper, child_wrapper, gc_wrapper)
|
||||
#
|
||||
# def check_bom_cost(self, fg_wrapper):
|
||||
# expected_values = {
|
||||
# "operating_cost": 15,
|
||||
# "raw_material_cost": 640,
|
||||
# "total_cost": 655
|
||||
# }
|
||||
#
|
||||
# for key in expected_values:
|
||||
# self.assertEqual(flt(expected_values[key]), flt(fg_wrapper.doc.fields.get(key)))
|
||||
#
|
||||
# def check_flat_bom(self, fg_wrapper, child_wrapper, gc_wrapper):
|
||||
# expected_flat_bom_items = {
|
||||
# ("Home Desktop 300", fg_wrapper.doc.name): (2, 20),
|
||||
# ("Home Desktop 100", fg_wrapper.doc.name): (1, 300),
|
||||
# ("Home Desktop 300", gc_wrapper.doc.name): (30, 10)
|
||||
# }
|
||||
#
|
||||
# self.assertEqual(len(fg_wrapper.doclist.get({"parentfield": "flat_bom_details"})), 3)
|
||||
#
|
||||
# for key, val in expected_flat_bom_items.items():
|
||||
# flat_bom = fg_wrapper.doclist.get({"parentfield": "flat_bom_details",
|
||||
# "item_code": key[0], "parent_bom": key[1]})[0]
|
||||
# self.assertEqual(val, (flat_bom.qty, flat_bom.rate))
|
||||
#
|
||||
#
|
||||
# def tearDown(self):
|
||||
# webnotes.conn.rollback()
|
@ -199,4 +199,5 @@ patch_list = [
|
||||
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Classic") # 2013-02-26',
|
||||
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Modern") # 2013-02-26',
|
||||
'execute:webnotes.reload_doc("accounts", "Print Format", "Sales Invoice Spartan") # 2013-02-26',
|
||||
"execute:(not webnotes.conn.exists('Role', 'Projects Manager')) and webnotes.doc({'doctype':'Role', 'role_name':'Projects Manager'}).insert()",
|
||||
]
|
@ -1,10 +0,0 @@
|
||||
[
|
||||
"BOM Replace Tool",
|
||||
"The BOM which will be replaced",
|
||||
"New BOM",
|
||||
"The new BOM after replacement",
|
||||
"Production",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM",
|
||||
"Replace",
|
||||
"Current BOM"
|
||||
]
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM \u0627\u0633\u062a\u0628\u062f\u0627\u0644 \u0623\u062f\u0627\u0629",
|
||||
"Current BOM": "BOM \u0627\u0644\u062d\u0627\u0644\u064a",
|
||||
"New BOM": "BOM \u062c\u062f\u064a\u062f\u0629",
|
||||
"Production": "\u0627\u0644\u0625\u0646\u062a\u0627\u062c",
|
||||
"Replace": "\u0627\u0633\u062a\u0628\u062f\u0644",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0627\u0633\u062a\u0628\u062f\u0627\u0644 BOM \u062e\u0627\u0635\u0629 \u0641\u064a \u062c\u0645\u064a\u0639 BOMs \u0627\u0644\u0623\u062e\u0631\u0649 \u0627\u0644\u062a\u064a \u064a\u0633\u062a\u062e\u062f\u0645 \u0641\u064a\u0647\u0627. \u0648\u0627\u0646\u0647 \u0633\u064a\u062d\u0644 \u0645\u062d\u0644 \u0627\u0644\u0631\u0627\u0628\u0637 BOM \u0627\u0644\u0642\u062f\u064a\u0645\u0629\u060c \u0648\u062a\u062d\u062f\u064a\u062b \u0648\u062a\u062c\u062f\u064a\u062f \u0627\u0644\u062a\u0643\u0644\u0641\u0629 "\u0627\u0644\u0628\u0646\u062f \u0627\u0646\u0641\u062c\u0627\u0631 BOM" \u0627\u0644\u062c\u062f\u0648\u0644 \u0627\u0644\u062c\u062f\u064a\u062f \u0648\u0641\u0642\u0627 BOM",
|
||||
"The BOM which will be replaced": "\u0648BOM \u0627\u0644\u062a\u064a \u0633\u064a\u062a\u0645 \u0627\u0633\u062a\u0628\u062f\u0627\u0644\u0647\u0627",
|
||||
"The new BOM after replacement": "\u0648BOM \u0627\u0644\u062c\u062f\u064a\u062f\u0629 \u0628\u0639\u062f \u0627\u0633\u062a\u0628\u062f\u0627\u0644"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM Tool Reemplazar",
|
||||
"Current BOM": "BOM actual",
|
||||
"New BOM": "Nueva lista de materiales",
|
||||
"Production": "Producci\u00f3n",
|
||||
"Replace": "Reemplazar",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Reemplazar un BOM particular en todas las listas de materiales de otros en los que se utiliza. Se sustituir\u00e1 el enlace BOM viejo, actualizar el costo y regenerar "Explosi\u00f3n lista de materiales Item" tabla seg\u00fan nueva lista de materiales",
|
||||
"The BOM which will be replaced": "La lista de materiales que ser\u00e1 sustituido",
|
||||
"The new BOM after replacement": "La lista de materiales nuevo despu\u00e9s de sustituirlo"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "Outil Remplacer BOM",
|
||||
"Current BOM": "Nomenclature actuelle",
|
||||
"New BOM": "Nouvelle nomenclature",
|
||||
"Production": "Production",
|
||||
"Replace": "Remplacer",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Remplacer une nomenclature particuli\u00e8re dans toutes les nomenclatures d'autres o\u00f9 il est utilis\u00e9. Il remplacera le lien de nomenclature ancienne, mettre \u00e0 jour les co\u00fbts et r\u00e9g\u00e9n\u00e9rer "Explosion de nomenclature article" la table comme pour une nouvelle nomenclature",
|
||||
"The BOM which will be replaced": "La nomenclature qui sera remplac\u00e9",
|
||||
"The new BOM after replacement": "La nouvelle nomenclature apr\u00e8s le remplacement"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "\u092c\u0940\u0913\u090f\u092e \u092c\u0926\u0932\u0947\u0902 \u0909\u092a\u0915\u0930\u0923",
|
||||
"Current BOM": "\u0935\u0930\u094d\u0924\u092e\u093e\u0928 \u092c\u0940\u0913\u090f\u092e",
|
||||
"New BOM": "\u0928\u0908 \u092c\u0940\u0913\u090f\u092e",
|
||||
"Production": "\u0909\u0924\u094d\u092a\u093e\u0926\u0928",
|
||||
"Replace": "\u092c\u0926\u0932\u0947\u0902",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0905\u0928\u094d\u092f \u0938\u092d\u0940 BOMs \u091c\u0939\u093e\u0902 \u092f\u0939 \u092a\u094d\u0930\u092f\u094b\u0917 \u0915\u093f\u092f\u093e \u091c\u093e\u0924\u093e \u0939\u0948 \u092e\u0947\u0902 \u090f\u0915 \u0935\u093f\u0936\u0947\u0937 \u092c\u0940\u0913\u090f\u092e \u092c\u0926\u0932\u0947\u0902. \u092f\u0939 \u092a\u0941\u0930\u093e\u0928\u0947 \u092c\u0940\u0913\u090f\u092e \u0932\u093f\u0902\u0915 \u0915\u0940 \u091c\u0917\u0939, \u0932\u093e\u0917\u0924 \u0905\u0926\u094d\u092f\u0924\u0928 \u0914\u0930 \u0928\u092f\u093e \u092c\u0940\u0913\u090f\u092e \u0915\u0947 \u0905\u0928\u0941\u0938\u093e\u0930 "BOM \u0927\u092e\u093e\u0915\u093e \u0906\u0907\u091f\u092e" \u0924\u093e\u0932\u093f\u0915\u093e \u092a\u0941\u0928\u0930\u094d\u091c\u0928\u094d\u092e",
|
||||
"The BOM which will be replaced": "\u092c\u0940\u0913\u090f\u092e \u091c\u094b \u092a\u094d\u0930\u0924\u093f\u0938\u094d\u0925\u093e\u092a\u093f\u0924 \u0915\u093f\u092f\u093e \u091c\u093e\u090f\u0917\u093e",
|
||||
"The new BOM after replacement": "\u092c\u0926\u0932\u0928\u0947 \u0915\u0947 \u092c\u093e\u0926 \u0928\u090f \u092c\u0940\u0913\u090f\u092e"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM Zamijenite alat",
|
||||
"Current BOM": "Trenutni BOM",
|
||||
"New BOM": "Novi BOM",
|
||||
"Production": "Proizvodnja",
|
||||
"Replace": "Zamijeniti",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Zamijenite odre\u0111eni BOM u svim drugim sastavnicama gdje se koriste. To \u0107e zamijeniti staru vezu BOM, a\u017eurirati tro\u0161kove i regenerirati "BOM eksploziju predmeta" stol kao i po novom BOM",
|
||||
"The BOM which will be replaced": "BOM koji \u0107e biti zamijenjen",
|
||||
"The new BOM after replacement": "Novi BOM nakon zamjene"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM Replace Tool",
|
||||
"Current BOM": "Actueel BOM",
|
||||
"New BOM": "Nieuwe BOM",
|
||||
"Production": "Productie",
|
||||
"Replace": "Vervang",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Vervang een bepaalde BOM in alle andere BOMs waar het wordt gebruikt. Deze vervangt de oude BOM link, updaten kosten en regenereren "BOM Explosion Item" tafel als per nieuwe BOM",
|
||||
"The BOM which will be replaced": "De BOM die zal worden vervangen",
|
||||
"The new BOM after replacement": "De nieuwe BOM na vervanging"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "Ferramenta de Substitui\u00e7\u00e3o da LDM",
|
||||
"Current BOM": "LDM atual",
|
||||
"New BOM": "Nova LDM",
|
||||
"Production": "Produ\u00e7\u00e3o",
|
||||
"Replace": "Substituir",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Substituir uma LDM espec\u00edfica em todas as LDMs outros onde ela \u00e9 usada. Isso ir\u00e1 substituir o link da LDM antiga, atualizar o custo e regenerar a tabela "Item de Explos\u00e3o da LDM" com a nova LDM",
|
||||
"The BOM which will be replaced": "A LDM que ser\u00e1 substitu\u00edda",
|
||||
"The new BOM after replacement": "A nova LDM ap\u00f3s substitui\u00e7\u00e3o"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM Ferramenta Substituir",
|
||||
"Current BOM": "BOM atual",
|
||||
"New BOM": "Novo BOM",
|
||||
"Production": "Produ\u00e7\u00e3o",
|
||||
"Replace": "Substituir",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "Substituir um BOM particular em todas as BOMs outros onde ele \u00e9 usado. Ele ir\u00e1 substituir o link BOM antigo, atualizar o custo e regenerar "Explos\u00e3o BOM Item" tabela como por novo BOM",
|
||||
"The BOM which will be replaced": "O BOM que ser\u00e1 substitu\u00eddo",
|
||||
"The new BOM after replacement": "O BOM novo ap\u00f3s substitui\u00e7\u00e3o"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "\u0411\u041e\u041c \u0417\u0430\u043c\u0435\u043d\u0430 \u0430\u043b\u0430\u0442\u0430",
|
||||
"Current BOM": "\u0422\u0440\u0435\u043d\u0443\u0442\u043d\u0438 \u0411\u041e\u041c",
|
||||
"New BOM": "\u041d\u043e\u0432\u0438 \u0411\u041e\u041c",
|
||||
"Production": "\u041f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u045a\u0430",
|
||||
"Replace": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u0438",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0417\u0430\u043c\u0435\u043d\u0438\u0442\u0435 \u043f\u043e\u0441\u0435\u0431\u043d\u0443 \u0431\u043e\u043c \u0443 \u0441\u0432\u0438\u043c \u043e\u0441\u0442\u0430\u043b\u0438\u043c \u0411\u041e\u041c\u0441 \u0433\u0434\u0435 \u0441\u0435 \u043e\u043d \u043a\u043e\u0440\u0438\u0441\u0442\u0438. \u041e\u043d\u0430 \u045b\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u0438 \u0441\u0442\u0430\u0440\u0443 \u0432\u0435\u0437\u0443 \u0431\u043e\u043c, \u0430\u0436\u0443\u0440\u0438\u0440\u0430\u045a\u0435 \u0442\u0440\u043e\u0448\u043a\u043e\u0432\u0435 \u0438 \u0440\u0435\u0433\u0435\u043d\u0435\u0440\u0438\u0448\u0443 "\u0431\u043e\u043c \u0435\u043a\u0441\u043f\u043b\u043e\u0437\u0438\u0458\u0435 \u0458\u0435\u0434\u0438\u043d\u0438\u0446\u0435" \u0442\u0430\u0431\u0435\u043b\u0443 \u043f\u043e \u043d\u043e\u0432\u043e\u043c \u0411\u041e\u041c",
|
||||
"The BOM which will be replaced": "\u0411\u041e\u041c \u043a\u043e\u0458\u0438 \u045b\u0435 \u0431\u0438\u0442\u0438 \u0437\u0430\u043c\u0435\u045a\u0435\u043d",
|
||||
"The new BOM after replacement": "\u041d\u043e\u0432\u0438 \u0411\u041e\u041c \u043d\u0430\u043a\u043e\u043d \u0437\u0430\u043c\u0435\u043d\u0435"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "BOM \u0baa\u0ba4\u0bbf\u0bb2\u0bbe\u0b95 \u0b95\u0bb0\u0bc1\u0bb5\u0bbf",
|
||||
"Current BOM": "\u0ba4\u0bb1\u0bcd\u0baa\u0bc7\u0bbe\u0ba4\u0bc8\u0baf BOM",
|
||||
"New BOM": "\u0baa\u0bc1\u0ba4\u0bbf\u0baf BOM",
|
||||
"Production": "\u0b89\u0bb1\u0bcd\u0baa\u0ba4\u0bcd\u0ba4\u0bbf",
|
||||
"Replace": "\u0baa\u0ba4\u0bbf\u0bb2\u0bbe\u0b95",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0b87\u0ba4\u0bc1 \u0baa\u0baf\u0ba9\u0bcd\u0baa\u0b9f\u0bc1\u0ba4\u0bcd\u0ba4\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0b95\u0bbf\u0bb1\u0ba4\u0bc1 \u0b85\u0bae\u0bc8\u0ba8\u0bcd\u0ba4\u0bc1\u0bb3\u0bcd\u0bb3 \u0b85\u0ba9\u0bc8\u0ba4\u0bcd\u0ba4\u0bc1 \u0bae\u0bb1\u0bcd\u0bb1 BOM \u0b95\u0bb3\u0bcd \u0b92\u0bb0\u0bc1 \u0b95\u0bc1\u0bb1\u0bbf\u0baa\u0bcd\u0baa\u0bbf\u0b9f\u0bcd\u0b9f BOM \u0baa\u0ba4\u0bbf\u0bb2\u0bbe\u0b95. \u0b87\u0ba4\u0bc1, \u0baa\u0bb4\u0bc8\u0baf BOM \u0b87\u0ba3\u0bc8\u0baa\u0bcd\u0baa\u0bc1 \u0baa\u0ba4\u0bbf\u0bb2\u0bbe\u0b95 \u0b9a\u0bc6\u0bb2\u0bb5\u0bc1 \u0baa\u0bc1\u0ba4\u0bc1\u0baa\u0bcd\u0baa\u0bbf\u0b95\u0bcd\u0b95 \u0baa\u0bc1\u0ba4\u0bbf\u0baf BOM \u0baa\u0b9f\u0bbf "BOM \u0bb5\u0bc6\u0b9f\u0bbf\u0baa\u0bcd\u0baa\u0bc1 \u0baa\u0bc6\u0bbe\u0bb0\u0bc1\u0bb3\u0bcd" \u0b85\u0b9f\u0bcd\u0b9f\u0bb5\u0ba3\u0bc8 \u0bae\u0bb1\u0bc1\u0b89\u0bb1\u0bcd\u0baa\u0ba4\u0bcd\u0ba4\u0bbf",
|
||||
"The BOM which will be replaced": "\u0baa\u0ba4\u0bbf\u0bb2\u0bc0\u0b9f\u0bc1 \u0b9a\u0bc6\u0baf\u0bcd\u0baf\u0baa\u0bcd\u0baa\u0b9f\u0bc1\u0bae\u0bcd BOM",
|
||||
"The new BOM after replacement": "\u0bae\u0bbe\u0bb1\u0bcd\u0bb1\u0bc1 \u0baa\u0bbf\u0ba9\u0bcd\u0ba9\u0bb0\u0bcd \u0baa\u0bc1\u0ba4\u0bbf\u0baf BOM"
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
{
|
||||
"BOM Replace Tool": "\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e21\u0e37\u0e2d\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48 BOM",
|
||||
"Current BOM": "BOM \u0e1b\u0e31\u0e08\u0e08\u0e38\u0e1a\u0e31\u0e19",
|
||||
"New BOM": "BOM \u0e43\u0e2b\u0e21\u0e48",
|
||||
"Production": "\u0e01\u0e32\u0e23\u0e1c\u0e25\u0e34\u0e15",
|
||||
"Replace": "\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48",
|
||||
"Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM": "\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48 BOM \u0e42\u0e14\u0e22\u0e40\u0e09\u0e1e\u0e32\u0e30\u0e43\u0e19 BOMs \u0e2d\u0e37\u0e48\u0e19 \u0e46 \u0e17\u0e31\u0e49\u0e07\u0e2b\u0e21\u0e14\u0e17\u0e35\u0e48\u0e16\u0e39\u0e01\u0e19\u0e33\u0e21\u0e32\u0e43\u0e0a\u0e49 \u0e21\u0e31\u0e19\u0e08\u0e30\u0e40\u0e02\u0e49\u0e32\u0e21\u0e32\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48\u0e01\u0e32\u0e23\u0e40\u0e0a\u0e37\u0e48\u0e2d\u0e21\u0e42\u0e22\u0e07 BOM \u0e40\u0e01\u0e48\u0e32\u0e1b\u0e23\u0e31\u0e1a\u0e1b\u0e23\u0e38\u0e07\u0e04\u0e48\u0e32\u0e43\u0e0a\u0e49\u0e08\u0e48\u0e32\u0e22\u0e41\u0e25\u0e30\u0e43\u0e2b\u0e49\u0e0a\u0e35\u0e27\u0e34\u0e15\u0e43\u0e2b\u0e21\u0e48 "\u0e23\u0e30\u0e40\u0e1a\u0e34\u0e14\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23 BOM" \u0e15\u0e32\u0e23\u0e32\u0e07\u0e40\u0e1b\u0e47\u0e19\u0e15\u0e48\u0e2d\u0e43\u0e2b\u0e21\u0e48 BOM",
|
||||
"The BOM which will be replaced": "BOM \u0e0b\u0e36\u0e48\u0e07\u0e08\u0e30\u0e16\u0e39\u0e01\u0e41\u0e17\u0e19\u0e17\u0e35\u0e48",
|
||||
"The new BOM after replacement": "BOM \u0e43\u0e2b\u0e21\u0e48\u0e2b\u0e25\u0e31\u0e07\u0e08\u0e32\u0e01\u0e40\u0e1b\u0e25\u0e35\u0e48\u0e22\u0e19"
|
||||
}
|
0
projects/doctype/time_log/__init__.py
Normal file
0
projects/doctype/time_log/__init__.py
Normal file
45
projects/doctype/time_log/time_log.py
Normal file
45
projects/doctype/time_log/time_log.py
Normal file
@ -0,0 +1,45 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
from webnotes import _
|
||||
|
||||
from webnotes.widgets.reportview import build_match_conditions
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
|
||||
def validate(self):
|
||||
self.validate_overlap()
|
||||
|
||||
def validate_overlap(self):
|
||||
existing = webnotes.conn.sql_list("""select name from `tabTime Log` where owner=%s and
|
||||
((from_time between %s and %s) or (to_time between %s and %s)) and name!=%s""",
|
||||
(self.doc.owner, self.doc.from_time, self.doc.to_time, self.doc.from_time,
|
||||
self.doc.to_time, self.doc.name))
|
||||
|
||||
if existing:
|
||||
webnotes.msgprint(_("This Time Log conflicts with") + ":" + ', '.join(existing),
|
||||
raise_exception=True)
|
||||
|
||||
@webnotes.whitelist()
|
||||
def get_events(start, end):
|
||||
match = build_match_conditions("Time Log")
|
||||
data = webnotes.conn.sql("""select name, from_time, to_time,
|
||||
activity_type, task, project from `tabTime Log`
|
||||
where from_time between '%(start)s' and '%(end)s' or to_time between '%(start)s' and '%(end)s'
|
||||
%(match)s""" % {
|
||||
"start": start,
|
||||
"end": end,
|
||||
"match": match and (" and " + match) or ""
|
||||
}, as_dict=True, update={"allDay": 0})
|
||||
|
||||
for d in data:
|
||||
d.title = d.name + ": " + d.activity_type
|
||||
if d.task:
|
||||
d.title += " for Task: " + d.task
|
||||
if d.project:
|
||||
d.title += " for Project: " + d.project
|
||||
|
||||
return data
|
121
projects/doctype/time_log/time_log.txt
Normal file
121
projects/doctype/time_log/time_log.txt
Normal file
@ -0,0 +1,121 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-02-26 14:58:28",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-02-26 16:09:53",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
{
|
||||
"autoname": "TL-.######",
|
||||
"description": "Log of Activities performed by users against Tasks that can be used for tracking time, billing.",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Master",
|
||||
"module": "Projects",
|
||||
"name": "__common__"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"name": "__common__",
|
||||
"parent": "Time Log",
|
||||
"parentfield": "fields",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"name": "__common__",
|
||||
"parent": "Time Log",
|
||||
"parentfield": "permissions",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0,
|
||||
"read": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocType",
|
||||
"name": "Time Log"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "from_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "From Time",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "to_time",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "To Time",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "activity_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Activity Type",
|
||||
"options": "Activity Type",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "task",
|
||||
"fieldtype": "Link",
|
||||
"label": "Task",
|
||||
"options": "Task"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "billable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Billable"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "note",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Note"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "section_break_9",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Project",
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "sales_invoice",
|
||||
"fieldtype": "Link",
|
||||
"label": "Sales Invoice",
|
||||
"options": "Sales Invoice"
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"match": "owner",
|
||||
"role": "Projects User"
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"role": "Projects Manager"
|
||||
}
|
||||
]
|
10
projects/doctype/time_log/time_log_calendar.js
Normal file
10
projects/doctype/time_log/time_log_calendar.js
Normal file
@ -0,0 +1,10 @@
|
||||
wn.views.calendar["Time Log"] = wn.views.Calendar.extend({
|
||||
field_map: {
|
||||
"start": "from_time",
|
||||
"end": "to_time",
|
||||
"id": "name",
|
||||
"title": "title",
|
||||
"allDay": "allDay"
|
||||
},
|
||||
get_events_method: "projects.doctype.time_log.time_log.get_events"
|
||||
})
|
@ -92,3 +92,4 @@ class DocType:
|
||||
|
||||
def on_cancel(self):
|
||||
webnotes.conn.set(self.doc, 'status', 'Cancelled')
|
||||
|
@ -1,4 +0,0 @@
|
||||
[
|
||||
"Projects Home",
|
||||
"Projects"
|
||||
]
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u0645\u0634\u0627\u0631\u064a\u0639",
|
||||
"Projects Home": "\u0635\u0641\u062d\u0629 \u0627\u0644\u0645\u0634\u0631\u0648\u0639\u0627\u062a"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Proyectos",
|
||||
"Projects Home": "Home Proyectos"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projets",
|
||||
"Projects Home": "Accueil Projets"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u092a\u0930\u093f\u092f\u094b\u091c\u0928\u093e\u0913\u0902",
|
||||
"Projects Home": "\u092a\u0930\u093f\u092f\u094b\u091c\u0928\u093e\u0913\u0902 \u0918\u0930"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projekti",
|
||||
"Projects Home": "Projekti Po\u010detna"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projecten",
|
||||
"Projects Home": "Projecten Home"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projetos",
|
||||
"Projects Home": "In\u00edcio de Projetos"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "Projetos",
|
||||
"Projects Home": "Home Projetos"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u041f\u0440\u043e\u0458\u0435\u043a\u0442\u0438",
|
||||
"Projects Home": "\u041f\u0440\u043e\u0458\u0435\u043a\u0442\u0438"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u0ba4\u0bbf\u0b9f\u0bcd\u0b9f\u0b99\u0bcd\u0b95\u0bb3\u0bcd",
|
||||
"Projects Home": "\u0ba4\u0bbf\u0b9f\u0bcd\u0b9f\u0b99\u0bcd\u0b95\u0bb3\u0bcd \u0bae\u0bc1\u0b95\u0baa\u0bcd\u0baa\u0bc1"
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"Projects": "\u0e42\u0e04\u0e23\u0e07\u0e01\u0e32\u0e23",
|
||||
"Projects Home": "\u0e2b\u0e19\u0e49\u0e32\u0e41\u0e23\u0e01\u0e42\u0e04\u0e23\u0e07\u0e01\u0e32\u0e23"
|
||||
}
|
@ -21,6 +21,11 @@ wn.module_page["Projects"] = [
|
||||
description: wn._("Timesheet for tasks."),
|
||||
doctype:"Timesheet"
|
||||
},
|
||||
{
|
||||
label: wn._("Time Log"),
|
||||
description: wn._("Time Log for tasks."),
|
||||
doctype:"Time Log"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -211,6 +211,12 @@ class DocType:
|
||||
'company_name':self.doc.name,
|
||||
'group_or_ledger':'Ledger',
|
||||
'parent_cost_center':'Root - ' + self.doc.abbr
|
||||
},
|
||||
{
|
||||
'cost_center_name': 'Auto Inventory Accounting',
|
||||
'company_name': self.doc.name,
|
||||
'group_or_ledger': 'Ledger',
|
||||
'parent_cost_center': 'Root - ' + self.doc.abbr
|
||||
}
|
||||
]
|
||||
for cc in cc_list:
|
||||
|
@ -30,7 +30,7 @@ def make_roles():
|
||||
"HR Manager", "HR User", "Leave Approver", "Maintenance Manager",
|
||||
"Maintenance User", "Manufacturing Manager", "Manufacturing User",
|
||||
"Material Manager", "Material Master Manager", "Material User",
|
||||
"Partner", "Projects User", "Purchase Manager", "Purchase Master Manager",
|
||||
"Partner", "Projects User", "Projects Manager", "Purchase Manager", "Purchase Master Manager",
|
||||
"Purchase User", "Quality Manager", "Sales Manager",
|
||||
"Sales Master Manager", "Sales User", "Supplier", "Support Manager",
|
||||
"Support Team", "Website Manager"]
|
||||
|
@ -17,64 +17,8 @@
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import webnotes
|
||||
import copy
|
||||
|
||||
from webnotes.model.bean import Bean
|
||||
from webnotes.model.doc import Document
|
||||
from webnotes.utils import flt
|
||||
|
||||
sql = webnotes.conn.sql
|
||||
|
||||
|
||||
class TestItem(unittest.TestCase):
|
||||
def setUp(self):
|
||||
webnotes.conn.begin()
|
||||
|
||||
def tearDown(self):
|
||||
webnotes.conn.rollback()
|
||||
|
||||
def testInsert(self):
|
||||
d = Bean()
|
||||
|
||||
count_before = flt(sql("select count(*) from tab"+_doctype)[0][0])
|
||||
if docok:
|
||||
for i in docok:
|
||||
d.doc = i
|
||||
d.children = None
|
||||
d.doc.fields['__islocal']=1
|
||||
d.save(1)
|
||||
count_after = flt(sql("select count(*) from tab"+_doctype)[0][0])
|
||||
self.assertTrue(count_before+len(docok)==count_after)
|
||||
|
||||
def testFailAssert(self):
|
||||
if docnotok:
|
||||
with self.assertRaises(Exception) as context:
|
||||
d = Bean()
|
||||
d.doc = docnotok[0]
|
||||
d.children = None
|
||||
d.doc.fields['__islocal']=1
|
||||
d.save(1)
|
||||
|
||||
# Test Data
|
||||
|
||||
tabOK = [
|
||||
{'is_purchase_item': None, 'is_pro_applicable': 'No', 'is_manufactured_item': None, 'description': 'Gel Ink', 'default_warehouse': None, 'item_name': 'Gel Ink', 'item_group': 'Ink', 'item_code': 'GELINK', 'is_sub_contracted_item': None, 'is_stock_item': 'Yes', 'stock_uom': 'Nos', 'docstatus': '0'},
|
||||
{'is_purchase_item': None, 'is_pro_applicable': 'No', 'is_manufactured_item': None, 'description': 'Gel Refill', 'default_warehouse': None, 'item_name': 'Gel Refill', 'item_group': 'Refill', 'item_code': 'GELREF', 'is_sub_contracted_item': None, 'is_stock_item': 'Yes', 'stock_uom': 'Nos', 'docstatus': '0'},
|
||||
{'is_purchase_item': None, 'is_pro_applicable': 'No', 'is_manufactured_item': None, 'description': 'Gel Pen', 'default_warehouse': None, 'item_name': 'Gel Pen', 'item_group': 'Pen', 'item_code': 'GELPEN', 'is_sub_contracted_item': None, 'is_stock_item': 'Yes', 'stock_uom': 'Nos', 'docstatus': '0'}
|
||||
]
|
||||
|
||||
tabNotOK = [
|
||||
{'is_purchase_item': None, 'is_pro_applicable': None, 'is_manufactured_item': None, 'description': 'F Ink', 'default_warehouse': None, 'item_name': 'F Ink', 'item_group': 'F Ink', 'item_code': None, 'is_sub_contracted_item': None, 'is_stock_item': 'No', 'stock_uom': 'Nos', 'docstatus': '0'}
|
||||
]
|
||||
|
||||
_doctype = 'Item'
|
||||
|
||||
for i in tabOK: i['doctype']=_doctype
|
||||
for i in tabNotOK: i['doctype']=_doctype
|
||||
|
||||
docok = [Document(fielddata=r) for r in tabOK]
|
||||
docnotok = [Document(fielddata=r) for r in tabNotOK]
|
||||
|
||||
test_ignore = ["BOM"]
|
||||
|
||||
test_records = [
|
||||
[{
|
||||
@ -164,4 +108,23 @@ test_records = [
|
||||
"is_sub_contracted_item": "No",
|
||||
"stock_uom": "_Test UOM"
|
||||
}],
|
||||
[{
|
||||
"doctype": "Item",
|
||||
"item_code": "_Test FG Item",
|
||||
"item_name": "_Test FG Item",
|
||||
"description": "_Test FG Item",
|
||||
"item_group": "_Test Item Group Desktops",
|
||||
"is_stock_item": "Yes",
|
||||
"is_asset_item": "No",
|
||||
"has_batch_no": "No",
|
||||
"has_serial_no": "No",
|
||||
"is_purchase_item": "Yes",
|
||||
"is_sales_item": "Yes",
|
||||
"is_service_item": "No",
|
||||
"is_sample_item": "No",
|
||||
"inspection_required": "No",
|
||||
"is_pro_applicable": "Yes",
|
||||
"is_sub_contracted_item": "Yes",
|
||||
"stock_uom": "_Test UOM"
|
||||
}],
|
||||
]
|
@ -18,10 +18,10 @@ from __future__ import unicode_literals
|
||||
import webnotes
|
||||
|
||||
from webnotes.utils import cstr, flt, cint
|
||||
from webnotes.model.doc import addchild
|
||||
from webnotes.model.bean import getlist
|
||||
from webnotes.model.code import get_obj
|
||||
from webnotes import msgprint
|
||||
from webnotes.model.doc import Document
|
||||
from webnotes import msgprint, _
|
||||
|
||||
sql = webnotes.conn.sql
|
||||
|
||||
@ -89,18 +89,6 @@ class DocType(BuyingController):
|
||||
webnotes.msgprint("Another Purchase Receipt using the same Challan No. already exists.\
|
||||
Please enter a valid Challan No.", raise_exception=1)
|
||||
|
||||
|
||||
# update valuation rate
|
||||
def update_valuation_rate(self):
|
||||
for d in self.doclist.get({"parentfield": "purchase_receipt_details"}):
|
||||
if d.qty:
|
||||
d.valuation_rate = (flt(d.purchase_rate) + flt(d.item_tax_amount)/flt(d.qty)
|
||||
+ flt(d.rm_supp_cost) / flt(d.qty)) / flt(d.conversion_factor)
|
||||
else:
|
||||
d.valuation_rate = 0.0
|
||||
|
||||
#check in manage account if purchase order required or not.
|
||||
# ====================================================================================
|
||||
def po_required(self):
|
||||
res = sql("select value from `tabSingles` where doctype = 'Global Defaults' and field = 'po_required'")
|
||||
if res and res[0][0]== 'Yes':
|
||||
@ -109,8 +97,6 @@ class DocType(BuyingController):
|
||||
msgprint("Purchse Order No. required against item %s"%d.item_code)
|
||||
raise Exception
|
||||
|
||||
|
||||
# validate
|
||||
def validate(self):
|
||||
super(DocType, self).validate()
|
||||
|
||||
@ -134,18 +120,17 @@ class DocType(BuyingController):
|
||||
pc_obj.validate_reference_value(self)
|
||||
self.check_for_stopped_status(pc_obj)
|
||||
|
||||
# update valuation rate
|
||||
self.update_valuation_rate()
|
||||
# sub-contracting
|
||||
self.validate_for_subcontracting()
|
||||
self.update_raw_materials_supplied()
|
||||
|
||||
self.update_valuation_rate("purchase_receipt_details")
|
||||
|
||||
# On Update
|
||||
# ----------------------------------------------------------------------------------------------------
|
||||
def on_update(self):
|
||||
if self.doc.rejected_warehouse:
|
||||
for d in getlist(self.doclist,'purchase_receipt_details'):
|
||||
d.rejected_warehouse = self.doc.rejected_warehouse
|
||||
|
||||
self.update_rw_material_detail()
|
||||
get_obj('Stock Ledger').scrub_serial_nos(self)
|
||||
self.scrub_rejected_serial_nos()
|
||||
|
||||
@ -303,118 +288,66 @@ class DocType(BuyingController):
|
||||
|
||||
self.make_gl_entries()
|
||||
|
||||
def validate_for_subcontracting(self):
|
||||
if self.sub_contracted_items and self.purchase_items and not self.doc.is_subcontracted:
|
||||
webnotes.msgprint(_("""Please enter whether Purchase Recipt is made for subcontracting
|
||||
or purchasing, in 'Is Subcontracted' field"""), raise_exception=1)
|
||||
|
||||
def update_rw_material_detail(self):
|
||||
if self.doc.is_subcontracted and not self.doc.supplier_warehouse:
|
||||
webnotes.msgprint(_("Please Enter Supplier Warehouse for subcontracted Items"),
|
||||
raise_exception=1)
|
||||
|
||||
for d in getlist(self.doclist,'purchase_receipt_details'):
|
||||
item_det = sql("select is_sub_contracted_item, is_purchase_item from `tabItem` where name = '%s'"%(d.item_code))
|
||||
def update_raw_materials_supplied(self):
|
||||
self.doclist = self.doc.clear_table(self.doclist, 'pr_raw_material_details')
|
||||
if self.sub_contracted_items:
|
||||
for item in self.doclist.get({"parentfield": "purchase_receipt_details"}):
|
||||
if item.item_code in self.sub_contracted_items:
|
||||
self.add_bom_items(item)
|
||||
|
||||
if item_det[0][0] == 'Yes':
|
||||
if item_det[0][1] == 'Yes':
|
||||
if not self.doc.is_subcontracted:
|
||||
msgprint("Please enter whether purchase receipt to be made for subcontracting or for purchase in 'Is Subcontracted' field .")
|
||||
raise Exception
|
||||
if self.doc.is_subcontracted == 'Yes':
|
||||
if not self.doc.supplier_warehouse:
|
||||
msgprint("Please Enter Supplier Warehouse for subcontracted Items")
|
||||
raise Exception
|
||||
self.add_bom(d)
|
||||
else:
|
||||
self.doclist = self.doc.clear_table(self.doclist,'pr_raw_material_details',1)
|
||||
self.doc.save()
|
||||
elif item_det[0][1] == 'No':
|
||||
if not self.doc.supplier_warehouse:
|
||||
msgprint("Please Enter Supplier Warehouse for subcontracted Items")
|
||||
raise Exception
|
||||
self.add_bom(d)
|
||||
def add_bom_items(self, d):
|
||||
bom_items = self.get_items_from_default_bom(d.item_code)
|
||||
raw_materials_cost = 0
|
||||
for item in bom_items:
|
||||
required_qty = flt(item.qty_consumed_per_unit) * flt(d.qty) * flt(d.conversion_factor)
|
||||
self.doclist.append({
|
||||
"parentfield": "pr_raw_material_details",
|
||||
"doctype": "Purchase Receipt Item Supplied",
|
||||
"reference_name": d.name,
|
||||
"bom_detail_no": item.name,
|
||||
"main_item_code": d.item_code,
|
||||
"rm_item_code": item.item_code,
|
||||
"description": item.description,
|
||||
"stock_uom": item.stock_uom,
|
||||
"required_qty": required_qty,
|
||||
"consumed_qty": required_qty,
|
||||
"conversion_factor": d.conversion_factor,
|
||||
"rate": item.rate,
|
||||
"amount": required_qty * flt(item.rate)
|
||||
})
|
||||
|
||||
self.delete_irrelevant_raw_material()
|
||||
#---------------calculate amt in Purchase Receipt Item Supplied-------------
|
||||
self.calculate_amount(d)
|
||||
raw_materials_cost += required_qty * flt(item.rate)
|
||||
|
||||
d.rm_supp_cost = raw_materials_cost
|
||||
|
||||
def add_bom(self, d):
|
||||
#----- fetching default bom from Bill of Materials instead of Item Master --
|
||||
bom_det = sql("""select t1.item, t2.item_code, t2.qty_consumed_per_unit,
|
||||
t2.moving_avg_rate, t2.value_as_per_mar, t2.stock_uom, t2.name, t2.description
|
||||
def get_items_from_default_bom(self, item_code):
|
||||
# print webnotes.conn.sql("""select name from `tabBOM` where item = '_Test FG Item'""")
|
||||
bom_items = sql("""select t2.item_code, t2.qty_consumed_per_unit,
|
||||
t2.rate, t2.stock_uom, t2.name, t2.description
|
||||
from `tabBOM` t1, `tabBOM Item` t2
|
||||
where t2.parent = t1.name and t1.item = %s and t1.is_default = 1
|
||||
and t1.docstatus = 1 and t2.docstatus =1 and t1.is_active = 1""", d.item_code)
|
||||
if not bom_det:
|
||||
msgprint("No default BOM exists for item: %s" % d.item_code)
|
||||
raise Exception
|
||||
else:
|
||||
#-------------- add child function--------------------
|
||||
chgd_rqd_qty = []
|
||||
for i in bom_det:
|
||||
and t1.docstatus = 1 and t1.is_active = 1""", item_code, as_dict=1)
|
||||
if not bom_items:
|
||||
msgprint(_("No default BOM exists for item: ") + item_code, raise_exception=1)
|
||||
|
||||
if i and not sql("select name from `tabPurchase Receipt Item Supplied` where reference_name = '%s' and bom_detail_no = '%s' and parent = '%s' " %(d.name, i[6], self.doc.name)):
|
||||
return bom_items
|
||||
|
||||
rm_child = addchild(self.doc, 'pr_raw_material_details', 'Purchase Receipt Item Supplied', self.doclist)
|
||||
|
||||
rm_child.reference_name = d.name
|
||||
rm_child.bom_detail_no = i and i[6] or ''
|
||||
rm_child.main_item_code = i and i[0] or ''
|
||||
rm_child.rm_item_code = i and i[1] or ''
|
||||
rm_child.description = i and i[7] or ''
|
||||
rm_child.stock_uom = i and i[5] or ''
|
||||
rm_child.rate = i and flt(i[3]) or flt(i[4])
|
||||
rm_child.conversion_factor = d.conversion_factor
|
||||
rm_child.required_qty = flt(i and flt(i[2]) or 0) * flt(d.qty) * flt(d.conversion_factor)
|
||||
rm_child.consumed_qty = flt(i and flt(i[2]) or 0) * flt(d.qty) * flt(d.conversion_factor)
|
||||
rm_child.amount = flt(flt(rm_child.consumed_qty)*flt(rm_child.rate))
|
||||
rm_child.save()
|
||||
chgd_rqd_qty.append(cstr(i[1]))
|
||||
else:
|
||||
act_qty = flt(i and flt(i[2]) or 0) * flt(d.qty) * flt(d.conversion_factor)
|
||||
for pr_rmd in getlist(self.doclist, 'pr_raw_material_details'):
|
||||
if i and i[6] == pr_rmd.bom_detail_no and (flt(act_qty) != flt(pr_rmd.required_qty) or i[1] != pr_rmd.rm_item_code or i[7] != pr_rmd.description):
|
||||
chgd_rqd_qty.append(cstr(i[1]))
|
||||
pr_rmd.main_item_code = i[0]
|
||||
pr_rmd.rm_item_code = i[1]
|
||||
pr_rmd.description = i[7]
|
||||
pr_rmd.stock_uom = i[5]
|
||||
pr_rmd.required_qty = flt(act_qty)
|
||||
pr_rmd.consumed_qty = flt(act_qty)
|
||||
pr_rmd.rate = i and flt(i[3]) or flt(i[4])
|
||||
pr_rmd.amount = flt(flt(pr_rmd.consumed_qty)*flt(pr_rmd.rate))
|
||||
pr_rmd.save()
|
||||
if chgd_rqd_qty:
|
||||
msgprint("Please check consumed quantity for Raw Material Item Code: '%s'in Raw materials Detail Table" % ((len(chgd_rqd_qty) > 1 and ','.join(chgd_rqd_qty[:-1]) +' and ' + cstr(chgd_rqd_qty[-1:][0]) ) or cstr(chgd_rqd_qty[0])))
|
||||
|
||||
|
||||
# Delete irrelevant raw material from PR Raw material details
|
||||
#--------------------------------------------------------------
|
||||
def delete_irrelevant_raw_material(self):
|
||||
for d in getlist(self.doclist,'pr_raw_material_details'):
|
||||
if not sql("select name from `tabPurchase Receipt Item` where name = '%s' and parent = '%s' and item_code = '%s'" % (d.reference_name, self.doc.name, d.main_item_code)):
|
||||
d.parent = 'old_par:'+self.doc.name
|
||||
d.save()
|
||||
|
||||
def calculate_amount(self, d):
|
||||
amt = 0
|
||||
for i in getlist(self.doclist,'pr_raw_material_details'):
|
||||
|
||||
if(i.reference_name == d.name):
|
||||
#if i.consumed_qty == 0:
|
||||
# msgprint("consumed qty cannot be 0. Please Enter consumed qty ")
|
||||
#raise Exception
|
||||
i.amount = flt(i.consumed_qty)* flt(i.rate)
|
||||
amt += i.amount
|
||||
d.rm_supp_cost = amt
|
||||
d.save()
|
||||
|
||||
|
||||
# --------------- Back Flush function called on submit and on cancel from update stock
|
||||
def bk_flush_supp_wh(self, is_submit):
|
||||
for d in getlist(self.doclist, 'pr_raw_material_details'):
|
||||
#--------- -ve quantity is passed as raw material qty has to be decreased when PR is submitted and it has to be increased when PR is cancelled
|
||||
# negative quantity is passed as raw material qty has to be decreased
|
||||
# when PR is submitted and it has to be increased when PR is cancelled
|
||||
consumed_qty = - flt(d.consumed_qty)
|
||||
self.make_sl_entry(d, self.doc.supplier_warehouse, flt(consumed_qty), 0, is_submit)
|
||||
|
||||
|
||||
# get current_stock
|
||||
# ----------------
|
||||
def get_current_stock(self):
|
||||
for d in getlist(self.doclist, 'pr_raw_material_details'):
|
||||
if self.doc.supplier_warehouse:
|
||||
|
@ -36,7 +36,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
|
||||
def test_purchase_receipt_gl_entry(self):
|
||||
webnotes.defaults.set_global_default("auto_inventory_accounting", 1)
|
||||
|
||||
self.assertEqual(cint(webnotes.defaults.get_global_default("auto_inventory_accounting")), 1)
|
||||
|
||||
pr = webnotes.bean(copy=test_records[0])
|
||||
@ -47,7 +46,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
gl_entries = webnotes.conn.sql("""select account, debit, credit
|
||||
from `tabGL Entry` where voucher_type='Purchase Receipt' and voucher_no=%s
|
||||
order by account desc""", pr.doc.name, as_dict=1)
|
||||
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
stock_in_hand_account = webnotes.conn.get_value("Company", pr.doc.company,
|
||||
@ -65,6 +63,17 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
|
||||
webnotes.defaults.set_global_default("auto_inventory_accounting", 0)
|
||||
|
||||
def test_subcontracting(self):
|
||||
pr = webnotes.bean(copy=test_records[1])
|
||||
pr.run_method("calculate_taxes_and_totals")
|
||||
pr.insert()
|
||||
|
||||
self.assertEquals(pr.doclist[1].rm_supp_cost, 70000.0)
|
||||
self.assertEquals(len(pr.doclist.get({"parentfield": "pr_raw_material_details"})), 2)
|
||||
|
||||
|
||||
test_dependencies = ["BOM"]
|
||||
|
||||
test_records = [
|
||||
[
|
||||
{
|
||||
@ -129,4 +138,36 @@ test_records = [
|
||||
"tax_amount": 150.0,
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"conversion_rate": 1.0,
|
||||
"currency": "INR",
|
||||
"doctype": "Purchase Receipt",
|
||||
"fiscal_year": "_Test Fiscal Year 2013",
|
||||
"posting_date": "2013-02-12",
|
||||
"posting_time": "15:33:30",
|
||||
"is_subcontracted": "Yes",
|
||||
"supplier_warehouse": "_Test Warehouse",
|
||||
"supplier": "_Test Supplier",
|
||||
"net_total": 5000.0,
|
||||
"grand_total": 5000.0,
|
||||
},
|
||||
{
|
||||
"conversion_factor": 1.0,
|
||||
"description": "_Test FG Item",
|
||||
"doctype": "Purchase Receipt Item",
|
||||
"item_code": "_Test FG Item",
|
||||
"item_name": "_Test FG Item",
|
||||
"parentfield": "purchase_receipt_details",
|
||||
"received_qty": 10.0,
|
||||
"qty": 10.0,
|
||||
"rejected_qty": 0.0,
|
||||
"import_rate": 500.0,
|
||||
"amount": 5000.0,
|
||||
"warehouse": "_Test Warehouse",
|
||||
"stock_uom": "Nos",
|
||||
"uom": "_Test UOM",
|
||||
}
|
||||
],
|
||||
]
|
@ -253,6 +253,9 @@ def get_outer_env(page_name, args):
|
||||
if k in settings.fields:
|
||||
args[k] = settings.fields.get(k)
|
||||
|
||||
for k in ["facebook_share", "google_plus_one", "twitter_share", "linked_in_share"]:
|
||||
args[k] = int(args.get(k) or 0)
|
||||
|
||||
if not args.brand_html:
|
||||
args.brand_html = "ERPNext"
|
||||
if not args.top_bar_background:
|
||||
|
Loading…
Reference in New Issue
Block a user