Merge branch 'master' into edge

This commit is contained in:
Anand Doshi 2013-02-27 19:14:16 +05:30
commit 215fc02395
49 changed files with 920 additions and 714 deletions

View File

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

View File

@ -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]:
@ -54,20 +54,32 @@ class DocType(BuyingController):
ret['due_date'] = add_days(cstr(self.doc.posting_date), acc and cint(acc[1]) or 0)
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:
self.validate_duplicate_docname('purchase_receipt')
@ -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,
@ -417,25 +437,31 @@ class DocType(BuyingController):
"cost_center": tax.cost_center
}, 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)
# item gl entries
stock_item_and_auto_accounting = False
stock_item_and_auto_inventory_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)
)
stock_item_and_auto_accounting = True
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)

View File

@ -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_purchase_invoice_calculation(self):
wrapper = webnotes.bean(self.get_test_doclist())
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)
# 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)
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(copy=test_records[0])
wrapper.run_method("calculate_taxes_and_totals")
wrapper.insert()
wrapper.load_from_db()
@ -91,145 +115,210 @@ class TestPurchaseInvoice(unittest.TestCase):
for i, item in enumerate(wrapper.doclist.get({"parentfield": "entries"})):
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"
},
{
"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
},
]
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",
"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
},
]
]

View File

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

View File

@ -86,4 +86,11 @@ class AccountsController(TransactionBase):
from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \
(", ".join((["%s"]*len(item_codes))),), item_codes)]
return self._stock_items
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

View File

@ -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):
@ -325,6 +327,16 @@ class BuyingController(AccountsController):
item.item_code in self.stock_items:
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):
@ -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

View File

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

View File

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

View File

@ -236,14 +236,11 @@ 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

View File

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

View File

@ -4,17 +4,19 @@ 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"):
fiscal_years = [filters["fiscal_year"]]
else:
fiscal_years = webnotes.conn.sql_list("select name from `tabFiscal Year` order by name desc")
employee_in = '", "'.join([e.name for e in employees])
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`

View File

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

View File

@ -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()",
]

View File

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

View File

@ -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 &quot;\u0627\u0644\u0628\u0646\u062f \u0627\u0646\u0641\u062c\u0627\u0631 BOM&quot; \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"
}

View File

@ -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 &quot;Explosi\u00f3n lista de materiales Item&quot; 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"
}

View File

@ -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&#39;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 &quot;Explosion de nomenclature article&quot; 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"
}

View File

@ -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 &quot;BOM \u0927\u092e\u093e\u0915\u093e \u0906\u0907\u091f\u092e&quot; \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"
}

View File

@ -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 &quot;BOM eksploziju predmeta&quot; 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"
}

View File

@ -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 &quot;BOM Explosion Item&quot; 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"
}

View File

@ -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 &quot;Item de Explos\u00e3o da LDM&quot; 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"
}

View File

@ -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 &quot;Explos\u00e3o BOM Item&quot; 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"
}

View File

@ -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 &quot;\u0431\u043e\u043c \u0435\u043a\u0441\u043f\u043b\u043e\u0437\u0438\u0458\u0435 \u0458\u0435\u0434\u0438\u043d\u0438\u0446\u0435&quot; \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"
}

View File

@ -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 &quot;BOM \u0bb5\u0bc6\u0b9f\u0bbf\u0baa\u0bcd\u0baa\u0bc1 \u0baa\u0bc6\u0bbe\u0bb0\u0bc1\u0bb3\u0bcd&quot; \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"
}

View File

@ -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 &quot;\u0e23\u0e30\u0e40\u0e1a\u0e34\u0e14\u0e23\u0e32\u0e22\u0e01\u0e32\u0e23 BOM&quot; \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"
}

View File

View 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

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

View 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"
})

View File

@ -91,4 +91,5 @@ class DocType:
webnotes.conn.set(self.doc, 'status', 'Submitted')
def on_cancel(self):
webnotes.conn.set(self.doc, 'status', 'Cancelled')
webnotes.conn.set(self.doc, 'status', 'Cancelled')

View File

@ -1,4 +0,0 @@
[
"Projects Home",
"Projects"
]

View File

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

View File

@ -1,4 +0,0 @@
{
"Projects": "Proyectos",
"Projects Home": "Home Proyectos"
}

View File

@ -1,4 +0,0 @@
{
"Projects": "Projets",
"Projects Home": "Accueil Projets"
}

View File

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

View File

@ -1,4 +0,0 @@
{
"Projects": "Projekti",
"Projects Home": "Projekti Po\u010detna"
}

View File

@ -1,4 +0,0 @@
{
"Projects": "Projecten",
"Projects Home": "Projecten Home"
}

View File

@ -1,4 +0,0 @@
{
"Projects": "Projetos",
"Projects Home": "In\u00edcio de Projetos"
}

View File

@ -1,4 +0,0 @@
{
"Projects": "Projetos",
"Projects Home": "Home Projetos"
}

View File

@ -1,4 +0,0 @@
{
"Projects": "\u041f\u0440\u043e\u0458\u0435\u043a\u0442\u0438",
"Projects Home": "\u041f\u0440\u043e\u0458\u0435\u043a\u0442\u0438"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
if self.doc.is_subcontracted and not self.doc.supplier_warehouse:
webnotes.msgprint(_("Please Enter Supplier Warehouse for subcontracted Items"),
raise_exception=1)
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)
def update_rw_material_detail(self):
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)
})
raw_materials_cost += required_qty * flt(item.rate)
d.rm_supp_cost = raw_materials_cost
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))
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)
self.delete_irrelevant_raw_material()
#---------------calculate amt in Purchase Receipt Item Supplied-------------
self.calculate_amount(d)
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)
return bom_items
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)):
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:

View File

@ -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,
@ -64,6 +62,17 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertEquals(expected_values[i][2], gle.credit)
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",
}
],
]

View File

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