Merge branch 'edge' of github.com:webnotes/erpnext into webshop
Conflicts:
patches/april_2013/p05_update_file_data.py
diff --git a/accounts/doctype/purchase_invoice/purchase_invoice.py b/accounts/doctype/purchase_invoice/purchase_invoice.py
index c53b6d9..b63a176 100644
--- a/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -466,9 +466,9 @@
# expense will be booked in sales invoice
stock_item_and_auto_inventory_accounting = True
- valuation_amt = (flt(item.amount, self.precision.item.amount) +
- flt(item.item_tax_amount, self.precision.item.item_tax_amount) +
- flt(item.rm_supp_cost, self.precision.item.rm_supp_cost))
+ valuation_amt = (flt(item.amount, self.precision("amount", item)) +
+ flt(item.item_tax_amount, self.precision("item_tax_amount", item)) +
+ flt(item.rm_supp_cost, self.precision("rm_supp_cost", item)))
gl_entries.append(
self.get_gl_dict({
diff --git a/accounts/doctype/sales_invoice/sales_invoice.py b/accounts/doctype/sales_invoice/sales_invoice.py
index d18b967..615da19 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/accounts/doctype/sales_invoice/sales_invoice.py
@@ -722,7 +722,7 @@
"against": self.doc.debit_to,
"credit": flt(tax.tax_amount),
"remarks": self.doc.remarks,
- "cost_center": tax.cost_center_other_charges
+ "cost_center": tax.cost_center
})
)
diff --git a/accounts/doctype/sales_invoice/sales_invoice.txt b/accounts/doctype/sales_invoice/sales_invoice.txt
index a2c422f..f1c0cab 100644
--- a/accounts/doctype/sales_invoice/sales_invoice.txt
+++ b/accounts/doctype/sales_invoice/sales_invoice.txt
@@ -1,8 +1,8 @@
[
{
- "creation": "2013-04-19 11:00:14",
+ "creation": "2013-05-06 12:03:41",
"docstatus": 0,
- "modified": "2013-04-22 11:59:28",
+ "modified": "2013-05-09 17:34:14",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -10,6 +10,7 @@
"allow_attach": 1,
"autoname": "naming_series:",
"doctype": "DocType",
+ "document_type": "Transaction",
"is_submittable": 1,
"module": "Accounts",
"name": "__common__",
@@ -30,6 +31,7 @@
"parent": "Sales Invoice",
"parentfield": "permissions",
"parenttype": "DocType",
+ "permlevel": 0,
"read": 1
},
{
@@ -251,7 +253,6 @@
"width": "50%"
},
{
- "description": "Will be calculated automatically when you enter the details",
"doctype": "DocField",
"fieldname": "net_total",
"fieldtype": "Currency",
@@ -259,12 +260,21 @@
"oldfieldname": "net_total",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "print_hide": 0,
+ "print_hide": 1,
"read_only": 1,
"reqd": 1
},
{
"doctype": "DocField",
+ "fieldname": "net_total_export",
+ "fieldtype": "Currency",
+ "label": "Net Total (Export)",
+ "options": "currency",
+ "print_hide": 0,
+ "read_only": 1
+ },
+ {
+ "doctype": "DocField",
"fieldname": "recalculate_values",
"fieldtype": "Button",
"label": "Re-Calculate Values",
@@ -1288,7 +1298,6 @@
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
- "permlevel": 0,
"report": 1,
"role": "Accounts User",
"submit": 1,
@@ -1297,8 +1306,7 @@
{
"doctype": "DocPerm",
"match": "customer",
- "permlevel": 0,
- "report": 1,
+ "report": 0,
"role": "Customer"
- },
+ }
]
\ No newline at end of file
diff --git a/accounts/doctype/sales_invoice/test_sales_invoice.py b/accounts/doctype/sales_invoice/test_sales_invoice.py
index b46cdd1..505848a 100644
--- a/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1,12 +1,226 @@
import webnotes
-import unittest
+import unittest, json
+from webnotes.utils import flt
class TestSalesInvoice(unittest.TestCase):
def make(self):
- w = webnotes.bean(webnotes.copy_doclist(test_records[0]))
+ w = webnotes.bean(copy=test_records[0])
w.insert()
w.submit()
return w
+
+ def test_sales_invoice_calculation_base_currency(self):
+ si = webnotes.bean(copy=test_records[2])
+ si.run_method("calculate_taxes_and_totals")
+ si.insert()
+
+ expected_values = {
+ "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount",
+ "base_ref_rate", "basic_rate", "amount"],
+ "_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500],
+ "_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750],
+ }
+
+ # check if children are saved
+ self.assertEquals(len(si.doclist.get({"parentfield": "entries"})),
+ len(expected_values)-1)
+
+ # check if item values are calculated
+ for d in si.doclist.get({"parentfield": "entries"}):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(d.fields.get(k), expected_values[d.item_code][i])
+
+ # check net total
+ self.assertEquals(si.doc.net_total, 1250)
+ self.assertEquals(si.doc.net_total_export, 1250)
+
+ # check tax calculation
+ expected_values = {
+ "keys": ["tax_amount", "total"],
+ "_Test Account Shipping Charges - _TC": [100, 1350],
+ "_Test Account Customs Duty - _TC": [125, 1475],
+ "_Test Account Excise Duty - _TC": [140, 1615],
+ "_Test Account Education Cess - _TC": [2.8, 1617.8],
+ "_Test Account S&H Education Cess - _TC": [1.4, 1619.2],
+ "_Test Account CST - _TC": [32.38, 1651.58],
+ "_Test Account VAT - _TC": [156.25, 1807.83],
+ "_Test Account Discount - _TC": [-180.78, 1627.05]
+ }
+
+ for d in si.doclist.get({"parentfield": "other_charges"}):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(d.fields.get(k), expected_values[d.account_head][i])
+
+ self.assertEquals(si.doc.grand_total, 1627.05)
+ self.assertEquals(si.doc.grand_total_export, 1627.05)
+
+ def test_sales_invoice_calculation_export_currency(self):
+ si = webnotes.bean(copy=test_records[2])
+ si.doc.currency = "USD"
+ si.doc.conversion_rate = 50
+ si.doclist[1].export_rate = 1
+ si.doclist[1].ref_rate = 1
+ si.doclist[2].export_rate = 3
+ si.doclist[2].ref_rate = 3
+ si.run_method("calculate_taxes_and_totals")
+ si.insert()
+
+ expected_values = {
+ "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount",
+ "base_ref_rate", "basic_rate", "amount"],
+ "_Test Item Home Desktop 100": [1, 0, 1, 10, 50, 50, 500],
+ "_Test Item Home Desktop 200": [3, 0, 3, 15, 150, 150, 750],
+ }
+
+ # check if children are saved
+ self.assertEquals(len(si.doclist.get({"parentfield": "entries"})),
+ len(expected_values)-1)
+
+ # check if item values are calculated
+ for d in si.doclist.get({"parentfield": "entries"}):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(d.fields.get(k), expected_values[d.item_code][i])
+
+ # check net total
+ self.assertEquals(si.doc.net_total, 1250)
+ self.assertEquals(si.doc.net_total_export, 25)
+
+ # check tax calculation
+ expected_values = {
+ "keys": ["tax_amount", "total"],
+ "_Test Account Shipping Charges - _TC": [100, 1350],
+ "_Test Account Customs Duty - _TC": [125, 1475],
+ "_Test Account Excise Duty - _TC": [140, 1615],
+ "_Test Account Education Cess - _TC": [2.8, 1617.8],
+ "_Test Account S&H Education Cess - _TC": [1.4, 1619.2],
+ "_Test Account CST - _TC": [32.38, 1651.58],
+ "_Test Account VAT - _TC": [156.25, 1807.83],
+ "_Test Account Discount - _TC": [-180.78, 1627.05]
+ }
+
+ for d in si.doclist.get({"parentfield": "other_charges"}):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(d.fields.get(k), expected_values[d.account_head][i])
+
+ self.assertEquals(si.doc.grand_total, 1627.05)
+ self.assertEquals(si.doc.grand_total_export, 32.54)
+
+ def test_inclusive_rate_validations(self):
+ si = webnotes.bean(copy=test_records[2])
+ for i, tax in enumerate(si.doclist.get({"parentfield": "other_charges"})):
+ tax.idx = i+1
+
+ si.doclist[1].export_rate = 62.5
+ si.doclist[1].export_rate = 191
+ for i in [3, 5, 6, 7, 8, 9]:
+ si.doclist[i].included_in_print_rate = 1
+
+ # tax type "Actual" cannot be inclusive
+ self.assertRaises(webnotes.ValidationError, si.run_method, "calculate_taxes_and_totals")
+
+ # taxes above included type 'On Previous Row Total' should also be included
+ si.doclist[3].included_in_print_rate = 0
+ self.assertRaises(webnotes.ValidationError, si.run_method, "calculate_taxes_and_totals")
+
+ def test_sales_invoice_calculation_base_currency_with_tax_inclusive_price(self):
+ # prepare
+ si = webnotes.bean(copy=test_records[3])
+ si.run_method("calculate_taxes_and_totals")
+ si.insert()
+
+ expected_values = {
+ "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount",
+ "base_ref_rate", "basic_rate", "amount"],
+ "_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 50, 50, 500],
+ "_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 150, 150, 750],
+ }
+
+ # check if children are saved
+ self.assertEquals(len(si.doclist.get({"parentfield": "entries"})),
+ len(expected_values)-1)
+
+ # check if item values are calculated
+ for d in si.doclist.get({"parentfield": "entries"}):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(d.fields.get(k), expected_values[d.item_code][i])
+
+ # check net total
+ self.assertEquals(si.doc.net_total, 1250)
+ self.assertEquals(si.doc.net_total_export, 1578.3)
+
+ # check tax calculation
+ expected_values = {
+ "keys": ["tax_amount", "total"],
+ "_Test Account Excise Duty - _TC": [140, 1390],
+ "_Test Account Education Cess - _TC": [2.8, 1392.8],
+ "_Test Account S&H Education Cess - _TC": [1.4, 1394.2],
+ "_Test Account CST - _TC": [27.88, 1422.08],
+ "_Test Account VAT - _TC": [156.25, 1578.33],
+ "_Test Account Customs Duty - _TC": [125, 1703.33],
+ "_Test Account Shipping Charges - _TC": [100, 1803.33],
+ "_Test Account Discount - _TC": [-180.33, 1623]
+ }
+
+ for d in si.doclist.get({"parentfield": "other_charges"}):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(flt(d.fields.get(k), 6), expected_values[d.account_head][i])
+
+ self.assertEquals(si.doc.grand_total, 1623)
+ self.assertEquals(si.doc.grand_total_export, 1623)
+
+ def test_sales_invoice_calculation_export_currency_with_tax_inclusive_price(self):
+ # prepare
+ si = webnotes.bean(copy=test_records[3])
+ si.doc.currency = "USD"
+ si.doc.conversion_rate = 50
+ si.doclist[1].export_rate = 50
+ si.doclist[1].adj_rate = 10
+ si.doclist[2].export_rate = 150
+ si.doclist[2].adj_rate = 20
+ si.doclist[9].rate = 5000
+
+ si.run_method("calculate_taxes_and_totals")
+ si.insert()
+
+ expected_values = {
+ "keys": ["ref_rate", "adj_rate", "export_rate", "export_amount",
+ "base_ref_rate", "basic_rate", "amount"],
+ "_Test Item Home Desktop 100": [55.56, 10, 50, 500, 2222.11, 1999.9, 19999.0],
+ "_Test Item Home Desktop 200": [187.5, 20, 150, 750, 7375.66, 5900.53, 29502.65],
+ }
+
+ # check if children are saved
+ self.assertEquals(len(si.doclist.get({"parentfield": "entries"})),
+ len(expected_values)-1)
+
+ # check if item values are calculated
+ for d in si.doclist.get({"parentfield": "entries"}):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(d.fields.get(k), expected_values[d.item_code][i])
+
+ # check net total
+ self.assertEquals(si.doc.net_total, 49501.65)
+ self.assertEquals(si.doc.net_total_export, 1250)
+
+ # check tax calculation
+ expected_values = {
+ "keys": ["tax_amount", "total"],
+ "_Test Account Excise Duty - _TC": [5540.22, 55041.87],
+ "_Test Account Education Cess - _TC": [110.81, 55152.68],
+ "_Test Account S&H Education Cess - _TC": [55.4, 55208.08],
+ "_Test Account CST - _TC": [1104.16, 56312.24],
+ "_Test Account VAT - _TC": [6187.71, 62499.95],
+ "_Test Account Customs Duty - _TC": [4950.17, 67450.12],
+ "_Test Account Shipping Charges - _TC": [5000, 72450.12],
+ "_Test Account Discount - _TC": [-7245.01, 65205.11]
+ }
+
+ for d in si.doclist.get({"parentfield": "other_charges"}):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(flt(d.fields.get(k), 6), expected_values[d.account_head][i])
+
+ self.assertEquals(si.doc.grand_total, 65205.11)
+ self.assertEquals(si.doc.grand_total_export, 1304.1)
def test_outstanding(self):
w = self.make()
@@ -520,4 +734,263 @@
"tax_amount": 50.0,
}
],
+ [
+ {
+ "naming_series": "_T-Sales Invoice-",
+ "company": "_Test Company",
+ "conversion_rate": 1.0,
+ "currency": "INR",
+ "debit_to": "_Test Customer - _TC",
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "doctype": "Sales Invoice",
+ "due_date": "2013-01-23",
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "grand_total_export": 0,
+ "plc_conversion_rate": 1.0,
+ "posting_date": "2013-01-23",
+ "price_list_currency": "INR",
+ "price_list_name": "_Test Price List",
+ "territory": "_Test Territory",
+ },
+ # items
+ {
+ "doctype": "Sales Invoice Item",
+ "parentfield": "entries",
+ "item_code": "_Test Item Home Desktop 100",
+ "item_name": "_Test Item Home Desktop 100",
+ "qty": 10,
+ "export_rate": 50,
+ "stock_uom": "_Test UOM",
+ "item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}),
+ "income_account": "Sales - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+
+ },
+ {
+ "doctype": "Sales Invoice Item",
+ "parentfield": "entries",
+ "item_code": "_Test Item Home Desktop 200",
+ "item_name": "_Test Item Home Desktop 200",
+ "qty": 5,
+ "export_rate": 150,
+ "stock_uom": "_Test UOM",
+ "income_account": "Sales - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+
+ },
+ # taxes
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "Actual",
+ "account_head": "_Test Account Shipping Charges - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Shipping Charges",
+ "rate": 100
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Customs Duty - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Customs Duty",
+ "rate": 10
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Excise Duty - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Excise Duty",
+ "rate": 12
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Amount",
+ "account_head": "_Test Account Education Cess - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Education Cess",
+ "rate": 2,
+ "row_id": 3
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "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",
+ "rate": 1,
+ "row_id": 3
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Total",
+ "account_head": "_Test Account CST - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "CST",
+ "rate": 2,
+ "row_id": 5
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account VAT - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "VAT",
+ "rate": 12.5
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Total",
+ "account_head": "_Test Account Discount - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Discount",
+ "rate": -10,
+ "row_id": 7
+ },
+ ],
+ [
+ {
+ "naming_series": "_T-Sales Invoice-",
+ "company": "_Test Company",
+ "conversion_rate": 1.0,
+ "currency": "INR",
+ "debit_to": "_Test Customer - _TC",
+ "customer": "_Test Customer",
+ "customer_name": "_Test Customer",
+ "doctype": "Sales Invoice",
+ "due_date": "2013-01-23",
+ "fiscal_year": "_Test Fiscal Year 2013",
+ "grand_total_export": 0,
+ "plc_conversion_rate": 1.0,
+ "posting_date": "2013-01-23",
+ "price_list_currency": "INR",
+ "price_list_name": "_Test Price List",
+ "territory": "_Test Territory",
+ },
+ # items
+ {
+ "doctype": "Sales Invoice Item",
+ "parentfield": "entries",
+ "item_code": "_Test Item Home Desktop 100",
+ "item_name": "_Test Item Home Desktop 100",
+ "qty": 10,
+ "export_rate": 62.5,
+ "stock_uom": "_Test UOM",
+ "item_tax_rate": json.dumps({"_Test Account Excise Duty - _TC": 10}),
+ "income_account": "Sales - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+
+ },
+ {
+ "doctype": "Sales Invoice Item",
+ "parentfield": "entries",
+ "item_code": "_Test Item Home Desktop 200",
+ "item_name": "_Test Item Home Desktop 200",
+ "qty": 5,
+ "export_rate": 190.66,
+ "stock_uom": "_Test UOM",
+ "income_account": "Sales - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+
+ },
+ # taxes
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Excise Duty - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Excise Duty",
+ "rate": 12,
+ "included_in_print_rate": 1,
+ "idx": 1
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Amount",
+ "account_head": "_Test Account Education Cess - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Education Cess",
+ "rate": 2,
+ "row_id": 1,
+ "included_in_print_rate": 1,
+ "idx": 2
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "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",
+ "rate": 1,
+ "row_id": 1,
+ "included_in_print_rate": 1,
+ "idx": 3
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Total",
+ "account_head": "_Test Account CST - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "CST",
+ "rate": 2,
+ "row_id": 3,
+ "included_in_print_rate": 1,
+ "idx": 4
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account VAT - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "VAT",
+ "rate": 12.5,
+ "included_in_print_rate": 1,
+ "idx": 5
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Net Total",
+ "account_head": "_Test Account Customs Duty - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Customs Duty",
+ "rate": 10,
+ "idx": 6
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "Actual",
+ "account_head": "_Test Account Shipping Charges - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Shipping Charges",
+ "rate": 100,
+ "idx": 7
+ },
+ {
+ "doctype": "Sales Taxes and Charges",
+ "parentfield": "other_charges",
+ "charge_type": "On Previous Row Total",
+ "account_head": "_Test Account Discount - _TC",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Discount",
+ "rate": -10,
+ "row_id": 7,
+ "idx": 8
+ },
+ ],
]
\ No newline at end of file
diff --git a/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt b/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt
index 161eb00..55a41a9 100644
--- a/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt
+++ b/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-02-22 01:27:41",
"docstatus": 0,
- "modified": "2013-04-17 14:05:18",
+ "modified": "2013-04-17 14:05:50",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -50,7 +50,7 @@
{
"default": ":Company",
"doctype": "DocField",
- "fieldname": "cost_center_other_charges",
+ "fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"oldfieldname": "cost_center_other_charges",
diff --git a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
index 1e72010..5787427 100644
--- a/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
+++ b/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js
@@ -140,7 +140,7 @@
return 'SELECT tabAccount.name FROM tabAccount WHERE tabAccount.group_or_ledger="Ledger" AND tabAccount.docstatus != 2 AND tabAccount.account_type in ("Tax", "Chargeable", "Income Account") AND tabAccount.company = "'+doc.company+'" AND tabAccount.name LIKE "%s"'
}
-cur_frm.fields_dict['other_charges'].grid.get_field("cost_center_other_charges").get_query = function(doc) {
+cur_frm.fields_dict['other_charges'].grid.get_field("cost_center").get_query = function(doc) {
return 'SELECT `tabCost Center`.`name` FROM `tabCost Center` WHERE `tabCost Center`.`company_name` = "' +doc.company+'" AND `tabCost Center`.%(key)s LIKE "%s" AND `tabCost Center`.`group_or_ledger` = "Ledger" AND `tabCost Center`.`docstatus`!= 2 ORDER BY `tabCost Center`.`name` ASC LIMIT 50';
}
diff --git a/buying/doctype/purchase_common/purchase_common.js b/buying/doctype/purchase_common/purchase_common.js
index dacee80..ff875ba 100644
--- a/buying/doctype/purchase_common/purchase_common.js
+++ b/buying/doctype/purchase_common/purchase_common.js
@@ -83,26 +83,48 @@
item_code: function(doc, cdt, cdn) {
var me = this;
- var item = locals[cdt][cdn];
+ var item = wn.model.get_doc(cdt, cdn);
+ // validate company
if(item.item_code) {
- this.frm.call({
- method: "buying.utils.get_item_details",
- child: item,
- args: {
- args: {
- doctype: me.frm.doc.doctype,
- docname: me.frm.doc.name,
- item_code: item.item_code,
- warehouse: item.warehouse,
- supplier: me.frm.doc.supplier,
- conversion_rate: me.frm.doc.conversion_rate,
- price_list_name: me.frm.doc.price_list_name,
- price_list_currency: me.frm.doc.price_list_currency,
- plc_conversion_rate: me.frm.doc.plc_conversion_rate
- }
- },
+ var fetch = true;
+ $.each(["company", "supplier"], function(i, fieldname) {
+ if(!me.frm.doc[fieldname]) {
+ fetch = false;
+ msgprint(wn._("Please specify") + ": " +
+ wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
+ ". " + wn._("It is needed to fetch Item Details."));
+ }
});
+
+ if(!fetch) {
+ item.item_code = null;
+ refresh_field("item_code", item.name, item.parentfield);
+ } else {
+ this.frm.call({
+ method: "buying.utils.get_item_details",
+ child: item,
+ args: {
+ args: {
+ item_code: item.item_code,
+ warehouse: item.warehouse,
+ doctype: me.frm.doc.doctype,
+ docname: me.frm.doc.name,
+ supplier: me.frm.doc.supplier,
+ conversion_rate: me.frm.doc.conversion_rate,
+ price_list_name: me.frm.doc.price_list_name,
+ price_list_currency: me.frm.doc.price_list_currency,
+ plc_conversion_rate: me.frm.doc.plc_conversion_rate,
+ is_subcontracted: me.frm.doc.is_subcontracted,
+ company: me.frm.doc.company,
+ currency: me.frm.doc.currency
+ }
+ },
+ callback: function(r) {
+ // TODO: calculate
+ }
+ });
+ }
}
},
diff --git a/buying/utils.py b/buying/utils.py
index 0431e64..54197b4 100644
--- a/buying/utils.py
+++ b/buying/utils.py
@@ -16,6 +16,7 @@
from __future__ import unicode_literals
import webnotes
+from webnotes import msgprint, _
from webnotes.utils import getdate, flt, add_days
import json
@@ -29,7 +30,11 @@
"warehouse": None,
"supplier": None,
"transaction_date": None,
- "conversion_rate": 1.0
+ "conversion_rate": 1.0,
+ "price_list_name": None,
+ "price_list_currency": None,
+ "plc_conversion_rate": 1.0,
+ "is_subcontracted": "Yes" / "No"
}
"""
if isinstance(args, basestring):
@@ -37,36 +42,14 @@
args = webnotes._dict(args)
- item_wrapper = webnotes.bean("Item", args.item_code)
- item = item_wrapper.doc
+ item_bean = webnotes.bean("Item", args.item_code)
+ item = item_bean.doc
- from stock.utils import validate_end_of_life
- validate_end_of_life(item.name, item.end_of_life)
+ _validate_item_details(args, item)
- # fetch basic values
- out = webnotes._dict()
- out.update({
- "item_name": item.item_name,
- "item_group": item.item_group,
- "brand": item.brand,
- "description": item.description,
- "qty": 0,
- "stock_uom": item.stock_uom,
- "uom": item.stock_uom,
- "conversion_factor": 1.0,
- "warehouse": args.warehouse or item.default_warehouse,
- "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
- item_wrapper.doclist.get({"parentfield": "item_tax"})))),
- "batch_no": None,
- "expense_head": item.purchase_account,
- "cost_center": item.cost_center
- })
+ out = _get_basic_details(args, item_bean)
- if args.supplier:
- item_supplier = item_wrapper.doclist.get({"parentfield": "item_supplier_details",
- "supplier": args.supplier})
- if item_supplier:
- out["supplier_part_no"] = item_supplier[0].supplier_part_no
+ out.supplier_part_no = _get_supplier_part_no(args, item_bean)
if out.warehouse:
out.projected_qty = webnotes.conn.get_value("Bin", {"item_code": item.name,
@@ -84,7 +67,7 @@
"Supplier Quotation"]:
# try fetching from price list
if args.price_list_name and args.price_list_currency:
- rates_as_per_price_list = get_rates_as_per_price_list(args, item_wrapper.doclist)
+ rates_as_per_price_list = get_rates_as_per_price_list(args, item_bean.doclist)
if rates_as_per_price_list:
out.update(rates_as_per_price_list)
@@ -95,6 +78,33 @@
out.update(last_purchase)
return out
+
+def _get_basic_details(args, item_bean):
+ item = item_bean.doc
+
+ out = webnotes._dict({
+ "description": item.description_html or item.description,
+ "qty": 0.0,
+ "uom": item.stock_uom,
+ "conversion_factor": 1.0,
+ "warehouse": args.warehouse or item.default_warehouse,
+ "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
+ item_bean.doclist.get({"parentfield": "item_tax"})))),
+ "batch_no": None,
+ "expense_head": item.purchase_account,
+ "cost_center": item.cost_center
+ })
+
+ for fieldname in ("item_name", "item_group", "brand", "stock_uom"):
+ out[fieldname] = item.fields.get(fieldname)
+
+ return out
+
+def _get_supplier_part_no(args, item_bean):
+ item_supplier = item_bean.doclist.get({"parentfield": "item_supplier_details",
+ "supplier": args.supplier})
+
+ return item_supplier and item_supplier[0].supplier_part_no or None
def get_rates_as_per_price_list(args, item_doclist=None):
if not item_doclist:
@@ -117,6 +127,21 @@
})
else:
return webnotes._dict()
+
+def _validate_item_details(args, item):
+ from utilities.transaction_base import validate_item_fetch
+ validate_item_fetch(args, item)
+
+ # validate if purchase item or subcontracted item
+ if item.is_purchase_item != "Yes":
+ msgprint(_("Item") + (" %s: " % item.name) + _("not a purchase item"),
+ raise_exception=True)
+
+ if args.is_subcontracted == "Yes" and item.is_sub_contracted_item != "Yes":
+ msgprint(_("Item") + (" %s: " % item.name) +
+ _("not a sub-contracted item.") +
+ _("Please select a sub-contracted item or do not sub-contract the transaction."),
+ raise_exception=True)
def get_last_purchase_details(item_code, doc_name, conversion_rate=1.0):
"""returns last purchase details in stock uom"""
diff --git a/controllers/accounts_controller.py b/controllers/accounts_controller.py
index 04e4bbd..19b2a50 100644
--- a/controllers/accounts_controller.py
+++ b/controllers/accounts_controller.py
@@ -97,4 +97,4 @@
if not hasattr(self, "_abbr"):
self._abbr = webnotes.conn.get_value("Company", self.doc.company, "abbr")
- return self._abbr
\ No newline at end of file
+ return self._abbr
diff --git a/controllers/buying_controller.py b/controllers/buying_controller.py
index 28d2db6..1fc411f 100644
--- a/controllers/buying_controller.py
+++ b/controllers/buying_controller.py
@@ -22,7 +22,7 @@
from buying.utils import get_item_details
from setup.utils import get_company_currency
-from webnotes.model.utils import round_floats_in_doc
+from utilities.transaction_base import validate_conversion_rate
from controllers.stock_controller import StockController
@@ -33,12 +33,11 @@
super(BuyingController, self).validate()
self.validate_stock_or_nonstock_items()
self.validate_warehouse_belongs_to_company()
+
if self.meta.get_field("currency"):
self.company_currency = get_company_currency(self.doc.company)
- self.validate_conversion_rate("currency", "conversion_rate")
-
- if self.doc.price_list_name and self.doc.price_list_currency:
- self.validate_conversion_rate("price_list_currency", "plc_conversion_rate")
+ validate_conversion_rate(self.doc.currency, self.doc.conversion_rate,
+ self.meta.get_label("conversion_rate"), self.doc.company)
# IMPORTANT: enable this only when client side code is similar to this one
# self.calculate_taxes_and_totals()
@@ -89,28 +88,6 @@
if not item.fields.get(r):
item.fields[r] = ret[r]
- def validate_conversion_rate(self, currency_field, conversion_rate_field):
- """common validation for currency and price list currency"""
-
- currency = self.doc.fields.get(currency_field)
- conversion_rate = flt(self.doc.fields.get(conversion_rate_field))
- conversion_rate_label = self.meta.get_label(conversion_rate_field)
-
- if conversion_rate == 0:
- msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True)
-
- # parenthesis for 'OR' are necessary as we want it to evaluate as
- # mandatory valid condition and (1st optional valid condition
- # or 2nd optional valid condition)
- valid_conversion_rate = (conversion_rate and
- ((currency == self.company_currency and conversion_rate == 1.00)
- or (currency != self.company_currency and conversion_rate != 1.00)))
-
- if not valid_conversion_rate:
- msgprint(_('Please enter valid ') + conversion_rate_label + (': ')
- + ("1 %s = [?] %s" % (currency, self.company_currency)),
- raise_exception=True)
-
def set_total_in_words(self):
from webnotes.utils import money_in_words
company_currency = get_company_currency(self.doc.company)
@@ -138,42 +115,40 @@
def _set_base(item, print_field, base_field):
"""set values in base currency"""
item.fields[base_field] = flt((flt(item.fields[print_field],
- self.precision.item[print_field]) * self.doc.conversion_rate),
- self.precision.item[base_field])
-
- for item in self.item_doclist:
- round_floats_in_doc(item, self.precision.item)
+ self.precision(print_field, item)) * self.doc.conversion_rate),
+ self.precision(base_field, item))
+
+ # hack! - cleaned up in _cleanup()
+ if self.doc.doctype != "Purchase Invoice":
+ df = self.meta.get_field("purchase_rate", parentfield=self.fname)
+ df.fieldname = "rate"
+ for item in self.item_doclist:
# hack! - cleaned up in _cleanup()
if self.doc.doctype != "Purchase Invoice":
item.rate = item.purchase_rate
- self.precision.item.rate = self.precision.item.purchase_rate
- item.discount = item.discount_rate
- self.precision.item.discount = self.precision.item.discount_rate
+ self.round_floats_in(item)
- if item.discount == 100:
- if not item.import_ref_rate:
- item.import_ref_rate = item.import_rate
+ if item.discount_rate == 100:
+ item.import_ref_rate = item.import_ref_rate or item.import_rate
item.import_rate = 0
else:
if item.import_ref_rate:
- item.import_rate = flt(item.import_ref_rate *
- (1.0 - (item.discount_rate / 100.0)),
- self.precision.item.import_rate)
+ item.import_rate = flt(item.import_ref_rate * (1.0 - (item.discount_rate / 100.0)),
+ self.precision("import_rate", item))
else:
- # assume that print rate and discount are specified
- item.import_ref_rate = flt(item.import_rate /
- (1.0 - (item.discount_rate / 100.0)),
- self.precision.item.import_ref_rate)
+ # assume that print rate and discount_rate are specified
+ item.import_ref_rate = flt(item.import_rate / (1.0 - (item.discount_rate / 100.0)),
+ self.precision("import_ref_rate", item))
item.import_amount = flt(item.import_rate * item.qty,
- self.precision.item.import_amount)
+ self.precision("import_amount", item))
_set_base(item, "import_ref_rate", "purchase_ref_rate")
_set_base(item, "import_rate", "rate")
_set_base(item, "import_amount", "amount")
-
+
def initialize_taxes(self):
for tax in self.tax_doclist:
# initialize totals to 0
@@ -186,7 +161,7 @@
self.validate_on_previous_row(tax)
- round_floats_in_doc(tax, self.precision.tax)
+ self.round_floats_in(tax)
def calculate_net_total(self):
self.doc.net_total = 0
@@ -196,9 +171,9 @@
self.doc.net_total += item.amount
self.doc.net_total_import += item.import_amount
- self.doc.net_total = flt(self.doc.net_total, self.precision.main.net_total)
+ self.doc.net_total = flt(self.doc.net_total, self.precision("net_total"))
self.doc.net_total_import = flt(self.doc.net_total_import,
- self.precision.main.net_total_import)
+ self.precision("net_total_import"))
def calculate_taxes(self):
for item in self.item_doclist:
@@ -216,7 +191,7 @@
# and tax.grand_total_for_current_item for the first such iteration
if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \
tax.charge_type=="Actual":
- zero_net_total_adjustment = flt(tax.rate, self.precision.tax.tax_amount)
+ zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax))
current_tax_amount += zero_net_total_adjustment
# store tax_amount for current item as it will be used for
@@ -238,12 +213,12 @@
# item's amount, previously applied tax and the current tax on that item
if i==0:
tax.grand_total_for_current_item = flt(item.amount +
- current_tax_amount, self.precision.tax.total)
+ current_tax_amount, self.precision("total", tax))
else:
tax.grand_total_for_current_item = \
flt(self.tax_doclist[i-1].grand_total_for_current_item +
- current_tax_amount, self.precision.tax.total)
+ current_tax_amount, self.precision("total", tax))
# in tax.total, accumulate grand total of each item
tax.total += tax.grand_total_for_current_item
@@ -255,20 +230,20 @@
def calculate_totals(self):
if self.tax_doclist:
self.doc.grand_total = flt(self.tax_doclist[-1].total,
- self.precision.main.grand_total)
+ self.precision("grand_total"))
self.doc.grand_total_import = flt(
self.doc.grand_total / self.doc.conversion_rate,
- self.precision.main.grand_total_import)
+ self.precision("grand_total_import"))
else:
self.doc.grand_total = flt(self.doc.net_total,
- self.precision.main.grand_total)
+ self.precision("grand_total"))
self.doc.grand_total_import = flt(
self.doc.grand_total / self.doc.conversion_rate,
- self.precision.main.grand_total_import)
+ self.precision("grand_total_import"))
self.doc.total_tax = \
flt(self.doc.grand_total - self.doc.net_total,
- self.precision.main.total_tax)
+ self.precision("total_tax"))
if self.meta.get_field("rounded_total"):
self.doc.rounded_total = round(self.doc.grand_total)
@@ -277,13 +252,13 @@
self.doc.rounded_total_import = round(self.doc.grand_total_import)
def calculate_outstanding_amount(self):
- if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus == 0:
+ if self.doc.doctype == "Purchase Invoice" and self.doc.docstatus < 2:
self.doc.total_advance = flt(self.doc.total_advance,
- self.precision.main.total_advance)
+ self.precision("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.precision("write_off_amount")), self.precision("total_amount_to_pay"))
self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
- self.precision.main.outstanding_amount)
+ self.precision("outstanding_amount"))
def _cleanup(self):
for tax in self.tax_doclist:
@@ -297,9 +272,11 @@
item.purchase_rate = item.rate
del item.fields["rate"]
- item.discount_rate = item.discount
- del item.fields["discount"]
-
+ # reset fieldname of rate
+ if self.doc.doctype != "Purchase Invoice":
+ df = self.meta.get_field("rate", parentfield=self.fname)
+ df.fieldname = "purchase_rate"
+
def validate_on_previous_row(self, tax):
"""
validate if a valid row id is mentioned in case of
@@ -325,7 +302,7 @@
if tax.charge_type == "Actual":
# distribute the tax amount proportionally to each item row
- actual = flt(tax.rate, self.precision.tax.tax_amount)
+ actual = flt(tax.rate, self.precision("tax_amount", tax))
current_tax_amount = (self.doc.net_total
and ((item.amount / self.doc.net_total) * actual)
or 0)
@@ -338,11 +315,11 @@
current_tax_amount = (tax_rate / 100.0) * \
self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item
- return flt(current_tax_amount, self.precision.tax.tax_amount)
+ return flt(current_tax_amount, self.precision("tax_amount", tax))
def _get_tax_rate(self, tax, item_tax_map):
if item_tax_map.has_key(tax.account_head):
- return flt(item_tax_map.get(tax.account_head), self.precision.tax.rate)
+ return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax))
else:
return tax.rate
@@ -356,26 +333,27 @@
if tax.category in ["Valuation", "Valuation and Total"] and \
item.item_code in self.stock_items:
item.item_tax_amount += flt(current_tax_amount,
- self.precision.item.item_tax_amount)
+ self.precision("item_tax_amount", item))
# update valuation rate
def update_valuation_rate(self, parentfield):
- for d in self.doclist.get({"parentfield": parentfield}):
- d.conversion_factor = d.conversion_factor or flt(webnotes.conn.get_value(
- "UOM Conversion Detail", {"parent": d.item_code, "uom": d.uom},
+ for item in self.doclist.get({"parentfield": parentfield}):
+ item.conversion_factor = item.conversion_factor or flt(webnotes.conn.get_value(
+ "UOM Conversion Detail", {"parent": item.item_code, "uom": item.uom},
"conversion_factor")) or 1
- if d.item_code and d.qty:
+
+ if item.item_code and item.qty:
+ self.round_floats_in(item)
+
+ purchase_rate = item.rate if self.doc.doctype == "Purchase Invoice" else item.purchase_rate
+
# if no item code, which is sometimes the case in purchase invoice,
# then it is not possible to track valuation against it
- d.valuation_rate = flt((flt(d.purchase_rate, self.precision.item.purchase_rate) or
- flt(d.rate, self.precision.item.rate) +
- (flt(d.item_tax_amount, self.precision.item.item_tax_amount) +
- flt(d.rm_supp_cost, self.precision.item.rm_supp_cost)) /
- flt(d.qty, self.precision.item.qty)) /
- flt(d.conversion_factor, self.precision.item.conversion_factor),
- self.precision.item.valuation_rate)
+ item.valuation_rate = flt((purchase_rate +
+ (item.item_tax_amount + item.rm_supp_cost) / item.qty) / item.conversion_factor,
+ self.precision("valuation_rate", item))
else:
- d.valuation_rate = 0.0
+ item.valuation_rate = 0.0
def validate_for_subcontracting(self):
if not self.doc.is_subcontracted and self.sub_contracted_items:
@@ -437,18 +415,6 @@
return bom_items
-
- @property
- def precision(self):
- if not hasattr(self, "_precision"):
- self._precision = webnotes._dict()
- self._precision.main = self.meta.get_precision_map()
- self._precision.item = self.meta.get_precision_map(parentfield = self.fname)
- if self.meta.get_field("purchase_tax_details"):
- 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"):
diff --git a/controllers/selling_controller.py b/controllers/selling_controller.py
index 80af337..63b87e1 100644
--- a/controllers/selling_controller.py
+++ b/controllers/selling_controller.py
@@ -16,9 +16,10 @@
from __future__ import unicode_literals
import webnotes
-from webnotes.utils import cint
+from webnotes.utils import cint, flt
from setup.utils import get_company_currency
from webnotes import msgprint, _
+import json
from controllers.stock_controller import StockController
@@ -71,4 +72,291 @@
if item.buying_amount and not item.cost_center:
msgprint(_("""Cost Center is mandatory for item: """) + item.item_code,
- raise_exception=1)
\ No newline at end of file
+ raise_exception=1)
+
+ def calculate_taxes_and_totals(self):
+ self.doc.conversion_rate = flt(self.doc.conversion_rate)
+ self.item_doclist = self.doclist.get({"parentfield": self.fname})
+ self.tax_doclist = self.doclist.get({"parentfield": "other_charges"})
+
+ self.calculate_item_values()
+ self.initialize_taxes()
+
+ self.determin_exclusive_rate()
+
+ # TODO
+ # code: save net_total_export on client side
+ # print format: show net_total_export instead of net_total
+
+ self.calculate_net_total()
+ self.calculate_taxes()
+ self.calculate_totals()
+ # self.calculate_outstanding_amount()
+
+ # TODO
+ # allocated amount of sales person
+ # total commission
+
+ self._cleanup()
+
+ def determin_exclusive_rate(self):
+ if not any((cint(tax.included_in_print_rate) for tax in self.tax_doclist)):
+ # no inclusive tax
+ return
+
+ for item in self.item_doclist:
+ item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
+ cumulated_tax_fraction = 0
+ for i, tax in enumerate(self.tax_doclist):
+ if cint(tax.included_in_print_rate):
+ tax.tax_fraction_for_current_item = \
+ self.get_current_tax_fraction(tax, item_tax_map)
+ else:
+ tax.tax_fraction_for_current_item = 0
+
+ if i==0:
+ tax.grand_total_fraction_for_current_item = 1 + \
+ tax.tax_fraction_for_current_item
+ else:
+ tax.grand_total_fraction_for_current_item = \
+ self.tax_doclist[i-1].grand_total_fraction_for_current_item \
+ + tax.tax_fraction_for_current_item
+
+ cumulated_tax_fraction += tax.tax_fraction_for_current_item
+
+ if cumulated_tax_fraction:
+ item.basic_rate = flt((item.export_rate * self.doc.conversion_rate) /
+ (1 + cumulated_tax_fraction), self.precision("basic_rate", item))
+
+ item.amount = flt(item.basic_rate * item.qty, self.precision("amount", item))
+
+ item.base_ref_rate = flt(item.basic_rate / (1 - (item.adj_rate / 100.0)),
+ self.precision("base_ref_rate", item))
+
+ def get_current_tax_fraction(self, tax, item_tax_map):
+ """
+ Get tax fraction for calculating tax exclusive amount
+ from tax inclusive amount
+ """
+ current_tax_fraction = 0
+
+ if cint(tax.included_in_print_rate):
+ tax_rate = self._get_tax_rate(tax, item_tax_map)
+
+ if tax.charge_type == "On Net Total":
+ current_tax_fraction = tax_rate / 100.0
+
+ elif tax.charge_type == "On Previous Row Amount":
+ current_tax_fraction = (tax_rate / 100.0) * \
+ self.tax_doclist[cint(tax.row_id) - 1].tax_fraction_for_current_item
+
+ elif tax.charge_type == "On Previous Row Total":
+ current_tax_fraction = (tax_rate / 100.0) * \
+ self.tax_doclist[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
+
+ return current_tax_fraction
+
+ def calculate_item_values(self):
+ def _set_base(item, print_field, base_field):
+ """set values in base currency"""
+ item.fields[base_field] = flt((flt(item.fields[print_field],
+ self.precision(print_field, item)) * self.doc.conversion_rate),
+ self.precision(base_field, item))
+
+ for item in self.item_doclist:
+ self.round_floats_in(item)
+
+ if item.adj_rate == 100:
+ item.ref_rate = item.ref_rate or item.export_rate
+ item.export_rate = 0
+ else:
+ if item.ref_rate:
+ item.export_rate = flt(item.ref_rate * (1.0 - (item.adj_rate / 100.0)),
+ self.precision("export_rate", item))
+ else:
+ # assume that print rate and discount are specified
+ item.ref_rate = flt(item.export_rate / (1.0 - (item.adj_rate / 100.0)),
+ self.precision("ref_rate", item))
+
+ item.export_amount = flt(item.export_rate * item.qty,
+ self.precision("export_amount", item))
+
+ _set_base(item, "ref_rate", "base_ref_rate")
+ _set_base(item, "export_rate", "basic_rate")
+ _set_base(item, "export_amount", "amount")
+
+ def initialize_taxes(self):
+ for tax in self.tax_doclist:
+ tax.tax_amount = tax.total = 0.0
+ # temporary fields
+ tax.tax_amount_for_current_item = tax.grand_total_for_current_item = 0.0
+ tax.item_wise_tax_detail = {}
+ self.validate_on_previous_row(tax)
+ self.validate_inclusive_tax(tax)
+ self.round_floats_in(tax)
+
+ def calculate_net_total(self):
+ self.doc.net_total = 0
+ self.doc.net_total_export = 0
+
+ for item in self.item_doclist:
+ self.doc.net_total += item.amount
+ self.doc.net_total_export += item.export_amount
+
+ self.doc.net_total = flt(self.doc.net_total, self.precision("net_total"))
+ self.doc.net_total_export = flt(self.doc.net_total_export,
+ self.precision("net_total_export"))
+
+ def calculate_taxes(self):
+ for item in self.item_doclist:
+ item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
+
+ for i, tax in enumerate(self.tax_doclist):
+ # tax_amount represents the amount of tax for the current step
+ current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
+
+ # case when net total is 0 but there is an actual type charge
+ # in this case add the actual amount to tax.tax_amount
+ # and tax.grand_total_for_current_item for the first such iteration
+ if not (current_tax_amount or self.doc.net_total or tax.tax_amount) and \
+ tax.charge_type=="Actual":
+ zero_net_total_adjustment = flt(tax.rate, self.precision("tax_amount", tax))
+ current_tax_amount += zero_net_total_adjustment
+
+ # store tax_amount for current item as it will be used for
+ # charge type = 'On Previous Row Amount'
+ tax.tax_amount_for_current_item = current_tax_amount
+
+ # accumulate tax amount into tax.tax_amount
+ tax.tax_amount += tax.tax_amount_for_current_item
+
+ # Calculate tax.total viz. grand total till that step
+ # note: grand_total_for_current_item contains the contribution of
+ # item's amount, previously applied tax and the current tax on that item
+ if i==0:
+ tax.grand_total_for_current_item = flt(item.amount +
+ current_tax_amount, self.precision("total", tax))
+
+ else:
+ tax.grand_total_for_current_item = \
+ flt(self.tax_doclist[i-1].grand_total_for_current_item +
+ current_tax_amount, self.precision("total", tax))
+
+ # in tax.total, accumulate grand total of each item
+ tax.total += tax.grand_total_for_current_item
+
+ # store tax_breakup for each item
+ # DOUBT: should valuation type amount also be stored?
+ tax.item_wise_tax_detail[item.item_code] = current_tax_amount
+
+ def calculate_totals(self):
+ self.doc.grand_total = flt(self.tax_doclist and \
+ self.tax_doclist[-1].total or self.doc.net_total, self.precision("grand_total"))
+ self.doc.grand_total_export = flt(self.doc.grand_total / self.doc.conversion_rate,
+ self.precision("grand_total_export"))
+
+ self.doc.rounded_total = round(self.doc.grand_total)
+ self.doc.rounded_total_export = round(self.doc.grand_total_export)
+
+ def get_current_tax_amount(self, item, tax, item_tax_map):
+ tax_rate = self._get_tax_rate(tax, item_tax_map)
+
+ if tax.charge_type == "Actual":
+ # distribute the tax amount proportionally to each item row
+ actual = flt(tax.rate, self.precision("tax_amount", tax))
+ current_tax_amount = (self.doc.net_total
+ and ((item.amount / self.doc.net_total) * actual)
+ or 0)
+ elif tax.charge_type == "On Net Total":
+ current_tax_amount = (tax_rate / 100.0) * item.amount
+ elif tax.charge_type == "On Previous Row Amount":
+ current_tax_amount = (tax_rate / 100.0) * \
+ self.tax_doclist[cint(tax.row_id) - 1].tax_amount_for_current_item
+ elif tax.charge_type == "On Previous Row Total":
+ current_tax_amount = (tax_rate / 100.0) * \
+ self.tax_doclist[cint(tax.row_id) - 1].grand_total_for_current_item
+
+ return flt(current_tax_amount, self.precision("tax_amount", tax))
+
+ def validate_on_previous_row(self, tax):
+ """
+ validate if a valid row id is mentioned in case of
+ On Previous Row Amount and On Previous Row Total
+ """
+ if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"] and \
+ (not tax.row_id or cint(tax.row_id) >= tax.idx):
+ msgprint((_("Row") + " # %(idx)s [%(taxes_doctype)s]: " + \
+ _("Please specify a valid") + " %(row_id_label)s") % {
+ "idx": tax.idx,
+ "taxes_doctype": tax.parenttype,
+ "row_id_label": self.meta.get_label("row_id",
+ parentfield="other_charges")
+ }, raise_exception=True)
+
+ def validate_inclusive_tax(self, tax):
+ def _on_previous_row_error(tax, row_range):
+ msgprint((_("Row")
+ + " # %(idx)s [%(taxes_doctype)s] [%(charge_type_label)s = \"%(charge_type)s\"]: "
+ + _("If:") + ' "%(inclusive_label)s" = ' + _("checked") + ", "
+ + _("then it is required that:") + " [" + _("Row") + " # %(row_range)s] "
+ + '"%(inclusive_label)s" = ' + _("checked")) % {
+ "idx": tax.idx,
+ "taxes_doctype": tax.doctype,
+ "inclusive_label": self.meta.get_label("included_in_print_rate",
+ parentfield="other_charges"),
+ "charge_type_label": self.meta.get_label("charge_type",
+ parentfield="other_charges"),
+ "charge_type": tax.charge_type,
+ "row_range": row_range
+ }, raise_exception=True)
+
+ if cint(tax.included_in_print_rate):
+ if tax.charge_type == "Actual":
+ # inclusive cannot be of type Actual
+ msgprint((_("Row")
+ + " # %(idx)s [%(taxes_doctype)s]: %(charge_type_label)s = \"%(charge_type)s\" "
+ + "cannot be included in Item's rate") % {
+ "idx": tax.idx,
+ "taxes_doctype": tax.doctype,
+ "charge_type_label": self.meta.get_label("charge_type",
+ parentfield="other_charges"),
+ "charge_type": tax.charge_type,
+ }, raise_exception=True)
+ elif tax.charge_type == "On Previous Row Amount" and \
+ not cint(self.tax_doclist[tax.row_id - 1].included_in_print_rate):
+ # referred row should also be inclusive
+ _on_previous_row_error(tax, tax.row_id)
+ elif tax.charge_type == "On Previous Row Total" and \
+ not all([cint(t.included_in_print_rate) for t in self.tax_doclist[:tax.idx - 1]]):
+ # all rows about this tax should be inclusive
+ _on_previous_row_error(tax, "1 - %d" % (tax.idx - 1,))
+
+ def _load_item_tax_rate(self, item_tax_rate):
+ if not item_tax_rate:
+ return {}
+ return json.loads(item_tax_rate)
+
+ def _get_tax_rate(self, tax, item_tax_map):
+ if item_tax_map.has_key(tax.account_head):
+ return flt(item_tax_map.get(tax.account_head), self.precision("rate", tax))
+ else:
+ return tax.rate
+
+ def _cleanup(self):
+ for tax in self.tax_doclist:
+ del tax.fields["grand_total_for_current_item"]
+ del tax.fields["tax_amount_for_current_item"]
+
+ for fieldname in ("tax_fraction_for_current_item",
+ "grand_total_fraction_for_current_item"):
+ if fieldname in tax.fields:
+ del tax.fields[fieldname]
+
+ tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail)
+
+ def validate_order_type(self):
+ valid_types = ["Sales", "Maintenance"]
+ if self.doc.order_type not in valid_types:
+ msgprint(_(self.meta.get_label("order_type")) + " " +
+ _("must be one of") + ": " + comma_or(valid_types),
+ raise_exception=True)
diff --git a/patches/april_2013/p05_update_file_data.py b/patches/april_2013/p05_update_file_data.py
index 39449a6..0168de6 100644
--- a/patches/april_2013/p05_update_file_data.py
+++ b/patches/april_2013/p05_update_file_data.py
@@ -1,5 +1,4 @@
import webnotes, webnotes.utils, os
-from webnotes.modules.export_file import export_to_files
def execute():
webnotes.reload_doc("core", "doctype", "file_data")
@@ -71,4 +70,4 @@
pass
else:
webnotes.conn.sql("""delete from `tabFile Data` where name=%s""",
- fileid)
\ No newline at end of file
+ fileid)
diff --git a/patches/april_2013/p06_default_cost_center.py b/patches/april_2013/p06_default_cost_center.py
index 4f80d95..4aaa7d5 100644
--- a/patches/april_2013/p06_default_cost_center.py
+++ b/patches/april_2013/p06_default_cost_center.py
@@ -1,10 +1,10 @@
import webnotes
def execute():
- for dt, fieldname in \
- (("Journal Voucher Detail", "cost_center"),
- ("Sales Taxes and Charges", "cost_center_other_charges"),
- ("Purchase Taxes and Charges", "cost_center"), ("Delivery Note Item", "cost_center"),
- ("Purchase Invoice Item", "cost_center"), ("Sales Invoice Item", "cost_center")):
- webnotes.conn.sql_ddl("""alter table `tab%s` alter `%s` drop default""" % (dt, fieldname))
+ webnotes.reload_doc("Stock", "DocType", "Delivery Note Item")
+ for dt in ("Journal Voucher Detail", "Sales Taxes and Charges",
+ "Purchase Taxes and Charges", "Delivery Note Item",
+ "Purchase Invoice Item", "Sales Invoice Item"):
+ webnotes.conn.sql_ddl("""alter table `tab%s` alter `cost_center` drop default""" \
+ % (dt,))
webnotes.reload_doc(webnotes.conn.get_value("DocType", dt, "module"), "DocType", dt)
diff --git a/patches/april_2013/p07_rename_cost_center_other_charges.py b/patches/april_2013/p07_rename_cost_center_other_charges.py
new file mode 100644
index 0000000..c3c9491
--- /dev/null
+++ b/patches/april_2013/p07_rename_cost_center_other_charges.py
@@ -0,0 +1,9 @@
+import webnotes
+
+def execute():
+ webnotes.reload_doc("Accounts", "DocType", "Sales Taxes and Charges")
+ webnotes.conn.sql("""update `tabSales Taxes and Charges`
+ set cost_center = cost_center_other_charges""")
+ webnotes.conn.sql_ddl("""alter table `tabSales Taxes and Charges`
+ drop column cost_center_other_charges""")
+
\ No newline at end of file
diff --git a/patches/may_2013/p01_selling_net_total_export.py b/patches/may_2013/p01_selling_net_total_export.py
new file mode 100644
index 0000000..dd0f68a
--- /dev/null
+++ b/patches/may_2013/p01_selling_net_total_export.py
@@ -0,0 +1,10 @@
+from __future__ import unicode_literals
+import webnotes
+
+def execute():
+ for module, doctype in (("Accounts", "Sales Invoice"), ("Selling", "Sales Order"), ("Selling", "Quotation"),
+ ("Stock", "Delivery Note")):
+ webnotes.reload_doc(module, "DocType", doctype)
+ webnotes.conn.sql("""update `tab%s`
+ set net_total_export = round(net_total / if(conversion_rate=0, 1, ifnull(conversion_rate, 1)), 2)""" %
+ (doctype,))
\ No newline at end of file
diff --git a/patches/patch_list.py b/patches/patch_list.py
index 1610009..5ceb232 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -240,12 +240,13 @@
"patches.april_2013.p06_update_file_size",
"patches.april_2013.p05_fixes_in_reverse_modules",
"execute:webnotes.delete_doc('DocType Mapper', 'Delivery Note-Packing Slip')",
- "execute:webnotes.reload_doc('Stock', 'DocType', 'Delivery Note Item')",
+ "patches.april_2013.p07_rename_cost_center_other_charges",
"patches.april_2013.p06_default_cost_center",
"execute:webnotes.reset_perms('File Data')",
"patches.april_2013.p07_update_file_data_2",
"patches.april_2013.rebuild_sales_browser",
"patches.april_2013.p08_price_list_country",
+ "patches.may_2013.p01_selling_net_total_export",
"patches.may_2013.repost_stock_for_no_posting_time",
"patches.may_2013.p01_conversion_factor_and_aii",
]
\ No newline at end of file
diff --git a/public/js/utils.js b/public/js/utils.js
index 4df9555..0284670 100644
--- a/public/js/utils.js
+++ b/public/js/utils.js
@@ -13,8 +13,7 @@
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-wn.provide('erpnext.utils');
+wn.provide("erpnext.utils");
erpnext.get_currency = function(company) {
if(!company && cur_frm)
@@ -23,4 +22,4 @@
return wn.model.get(":Company", company).default_currency || wn.boot.sysdefaults.currency;
else
return wn.boot.sysdefaults.currency;
-}
\ No newline at end of file
+}
diff --git a/selling/doctype/quotation/quotation.py b/selling/doctype/quotation/quotation.py
index c154a6a..7e83131 100644
--- a/selling/doctype/quotation/quotation.py
+++ b/selling/doctype/quotation/quotation.py
@@ -142,6 +142,8 @@
#do not allow sales item in maintenance quotation and service item in sales quotation
#-----------------------------------------------------------------------------------------------
def validate_order_type(self):
+ super(DocType, self).validate_order_type()
+
if self.doc.order_type in ['Maintenance', 'Service']:
for d in getlist(self.doclist, 'quotation_details'):
is_service_item = sql("select is_service_item from `tabItem` where name=%s", d.item_code)
diff --git a/selling/doctype/quotation/quotation.txt b/selling/doctype/quotation/quotation.txt
index feda14c..24a080b 100644
--- a/selling/doctype/quotation/quotation.txt
+++ b/selling/doctype/quotation/quotation.txt
@@ -1,8 +1,8 @@
[
{
- "creation": "2013-04-03 09:10:44",
+ "creation": "2013-05-06 12:03:40",
"docstatus": 0,
- "modified": "2013-04-03 09:58:02",
+ "modified": "2013-05-06 13:07:37",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -239,13 +239,21 @@
"oldfieldname": "net_total",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "print_hide": 0,
+ "print_hide": 1,
"read_only": 1,
"reqd": 0,
"width": "100px"
},
{
"doctype": "DocField",
+ "fieldname": "net_total_export",
+ "fieldtype": "Currency",
+ "label": "Net Total (Export)",
+ "options": "currency",
+ "read_only": 1
+ },
+ {
+ "doctype": "DocField",
"fieldname": "recalculate_values",
"fieldtype": "Button",
"label": "Re-Calculate Values",
diff --git a/selling/doctype/sales_common/sales_common.js b/selling/doctype/sales_common/sales_common.js
index 8a8d8d0..67c7539 100644
--- a/selling/doctype/sales_common/sales_common.js
+++ b/selling/doctype/sales_common/sales_common.js
@@ -21,6 +21,83 @@
// cur_frm.cscript.other_fname - wn.require('app/accounts/doctype/sales_taxes_and_charges_master/sales_taxes_and_charges_master.js'); fieldname
// cur_frm.cscript.sales_team_fname - Sales Team fieldname
+wn.provide("erpnext.selling");
+
+erpnext.selling.SellingController = wn.ui.form.Controller.extend({
+ setup: function() {
+
+ },
+
+ refresh: function() {
+
+ },
+
+ item_code: function(doc, cdt, cdn) {
+ var me = this;
+ var item = wn.model.get_doc(cdt, cdn);
+ if(item.item_code) {
+ var fetch = true;
+ $.each(["company", "customer"], function(i, fieldname) {
+ if(!me.frm.doc[fieldname]) {
+ fetch = false;
+ msgprint(wn._("Please specify") + ": " +
+ wn.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
+ ". " + wn._("It is needed to fetch Item Details."));
+ }
+ });
+
+ if(!fetch) {
+ item.item_code = null;
+ refresh_field("item_code", item.name, item.parentfield);
+ } else {
+ this.frm.call({
+ method: "selling.utils.get_item_details",
+ child: item,
+ args: {
+ args: {
+ item_code: item.item_code,
+ warehouse: item.warehouse,
+ doctype: me.frm.doc.doctype,
+ customer: me.frm.doc.customer,
+ currency: me.frm.doc.currency,
+ conversion_rate: me.frm.doc.conversion_rate,
+ price_list_name: me.frm.doc.price_list_name,
+ price_list_currency: me.frm.doc.price_list_currency,
+ plc_conversion_rate: me.frm.doc.plc_conversion_rate,
+ company: me.frm.doc.company,
+ order_type: me.frm.doc.order_type
+
+ }
+ },
+ callback: function(r) {
+ // TODO: calculate
+ }
+ });
+ }
+ }
+ },
+
+ update_item_details: function() {
+
+ },
+
+ set_dynamic_labels: function() {
+
+ },
+
+
+});
+
+// to save previous state of cur_frm.cscript
+var prev_cscript = {};
+$.extend(prev_cscript, cur_frm.cscript);
+
+cur_frm.cscript = new erpnext.selling.SellingController({frm: cur_frm});
+
+// for backward compatibility: combine new and previous states
+$.extend(cur_frm.cscript, prev_cscript);
+
+
// ============== Load Default Taxes ===================
cur_frm.cscript.load_taxes = function(doc, cdt, cdn, callback) {
// run if this is not executed from dt_map...
@@ -264,7 +341,7 @@
// ******************** ITEM CODE ********************************
cur_frm.fields_dict[cur_frm.cscript.fname].grid.get_field("item_code").get_query = function(doc, cdt, cdn) {
- if (inList(['Maintenance', 'Service'], doc.order_type)) {
+ if (doc.order_type == "Maintenance") {
return erpnext.queries.item({
'ifnull(tabItem.is_service_item, "No")': 'Yes'
});
@@ -275,34 +352,6 @@
}
}
-
-cur_frm.cscript.item_code = function(doc, cdt, cdn) {
- var fname = cur_frm.cscript.fname;
- var d = locals[cdt][cdn];
- if (d.item_code) {
- if (!doc.company) {
- msgprint("Please select company to proceed");
- d.item_code = '';
- refresh_field('item_code', d.name, fname);
- } else {
- var callback = function(r, rt){
- cur_frm.cscript.recalc(doc, 1);
- }
- var args = {
- 'item_code':d.item_code,
- 'income_account':d.income_account,
- 'cost_center': d.cost_center,
- 'warehouse': d.warehouse
- };
- get_server_fields('get_item_details',JSON.stringify(args),
- fname,doc,cdt,cdn,1,callback);
- }
- }
- if(cur_frm.cscript.custom_item_code){
- cur_frm.cscript.custom_item_code(doc, cdt, cdn);
- }
-}
-
//Barcode
//
cur_frm.cscript.barcode = function(doc, cdt, cdn) {
@@ -518,6 +567,8 @@
if(flt(doc.conversion_rate)>1) {
net_total_incl *= flt(doc.conversion_rate);
}
+
+ // TODO: store net_total_export
doc.net_total = inclusive_rate ? flt(net_total_incl) : flt(net_total);
doc.other_charges_total = roundNumber(flt(other_charges_total), 2);
diff --git a/selling/doctype/sales_order/sales_order.py b/selling/doctype/sales_order/sales_order.py
index 6a52e5a..c74e7e1 100644
--- a/selling/doctype/sales_order/sales_order.py
+++ b/selling/doctype/sales_order/sales_order.py
@@ -194,6 +194,8 @@
and current Sales Order""" % (self.doc.order_type, d.prevdoc_docname))
def validate_order_type(self):
+ super(DocType, self).validate_order_type()
+
#validate delivery date
if self.doc.order_type == 'Sales' and not self.doc.delivery_date:
msgprint("Please enter 'Expected Delivery Date'")
diff --git a/selling/doctype/sales_order/sales_order.txt b/selling/doctype/sales_order/sales_order.txt
index ba0b1de..9780dc7 100644
--- a/selling/doctype/sales_order/sales_order.txt
+++ b/selling/doctype/sales_order/sales_order.txt
@@ -1,8 +1,8 @@
[
{
- "creation": "2013-03-07 14:48:34",
+ "creation": "2013-05-06 12:03:43",
"docstatus": 0,
- "modified": "2013-01-29 17:14:58",
+ "modified": "2013-05-06 13:06:37",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -251,13 +251,21 @@
"oldfieldname": "net_total",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "print_hide": 0,
+ "print_hide": 1,
"read_only": 1,
"reqd": 0,
"width": "150px"
},
{
"doctype": "DocField",
+ "fieldname": "net_total_export",
+ "fieldtype": "Currency",
+ "label": "Net Total (Export)",
+ "options": "currency",
+ "read_only": 1
+ },
+ {
+ "doctype": "DocField",
"fieldname": "recalculate_values",
"fieldtype": "Button",
"label": "Re-Calculate Values",
@@ -955,7 +963,6 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "match": "",
"permlevel": 1,
"report": 0,
"role": "Sales Manager",
@@ -978,7 +985,6 @@
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
- "match": "",
"permlevel": 0,
"report": 1,
"role": "Sales User",
@@ -990,7 +996,6 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "match": "",
"permlevel": 1,
"report": 0,
"role": "Sales User",
@@ -1013,7 +1018,6 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "match": "",
"permlevel": 1,
"role": "Maintenance Manager",
"submit": 0
@@ -1034,7 +1038,6 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "match": "",
"permlevel": 1,
"role": "Maintenance User",
"submit": 0
diff --git a/selling/utils.py b/selling/utils.py
index 21e94f7..23574df 100644
--- a/selling/utils.py
+++ b/selling/utils.py
@@ -16,6 +16,9 @@
from __future__ import unicode_literals
import webnotes
+from webnotes import msgprint, _
+from webnotes.utils import flt
+import json
def get_customer_list(doctype, txt, searchfield, start, page_len, filters):
if webnotes.conn.get_default("cust_master_name") == "Customer Name":
@@ -29,4 +32,100 @@
case when customer_name like %s then 0 else 1 end,
name, customer_name limit %s, %s""" %
(", ".join(fields), searchfield, "%s", "%s", "%s", "%s", "%s", "%s"),
- ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len))
\ No newline at end of file
+ ("%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, "%%%s%%" % txt, start, page_len))
+
+@webnotes.whitelist()
+def get_item_details(args):
+ """
+ args = {
+ "item_code": "",
+ "warehouse": None,
+ "customer": "",
+ "conversion_rate": 1.0,
+ "price_list_name": None,
+ "price_list_currency": None,
+ "plc_conversion_rate": 1.0
+ }
+ """
+ if isinstance(args, basestring):
+ args = json.loads(args)
+ args = webnotes._dict(args)
+
+ item_bean = webnotes.bean("Item", args.item_code)
+
+ _validate_item_details(args, item_bean.doc)
+
+ out = _get_basic_details(args, item_bean)
+
+ if args.price_list_name and args.price_list_currency:
+ out.update(_get_price_list_rate(args, item_bean))
+
+ if out.warehouse or out.reserved_warehouse:
+ out.update(_get_available_qty(args, out.warehouse or out.reserved_warehouse))
+
+ out.customer_item_code = _get_customer_item_code(args, item_bean)
+
+ return out
+
+def _validate_item_details(args, item):
+ from utilities.transaction_base import validate_item_fetch
+ validate_item_fetch(args, item)
+
+ # validate if sales item or service item
+ if args.order_type == "Maintenance":
+ if item.is_service_item != "Yes":
+ msgprint(_("Item") + (" %s: " % item.name) +
+ _("not a service item.") +
+ _("Please select a service item or change the order type to Sales."),
+ raise_exception=True)
+
+ elif item.is_sales_item != "Yes":
+ msgprint(_("Item") + (" %s: " % item.name) + _("not a sales item"),
+ raise_exception=True)
+
+def _get_basic_details(args, item_bean):
+ item = item_bean.doc
+ out = webnotes._dict({
+ "description": item.description_html or item.description,
+ "reserved_warehouse": item.default_warehouse,
+ "warehouse": item.default_warehouse or args.warehouse,
+ "income_account": item.default_income_account or args.income_account,
+ "expense_account": item.purchase_account or args.expense_account,
+ "cost_center": item.default_sales_cost_center or args.cost_center,
+ "qty": 1.0,
+ "adj_rate": 0.0,
+ "export_amount": 0.0,
+ "amount": 0.0,
+ "batch_no": None,
+ "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in
+ item_bean.doclist.get({"parentfield": "item_tax"})))),
+ })
+
+ for fieldname in ("item_name", "item_group", "barcode", "brand", "stock_uom"):
+ out[fieldname] = item.fields.get(fieldname)
+
+ return out
+
+def _get_price_list_rate(args, item_bean):
+ base_ref_rate = item_bean.doclist.get({
+ "parentfield": "ref_rate_details",
+ "price_list_name": args.price_list_name,
+ "price_list_currency": args.price_list_currency,
+ "selling": 1})
+ out = webnotes._dict()
+ out.base_ref_rate = flt(base_ref_rate[0].ref_rate) if base_ref_rate else 0.0
+ out.basic_rate = out.base_ref_rate
+ out.ref_rate = out.base_ref_rate / flt(args.conversion_rate)
+ out.export_rate = out.ref_rate
+ return out
+
+def _get_available_qty(args, warehouse):
+ return webnotes.conn.get_value("Bin", {"item_code": args.item_code, "warehouse": warehouse},
+ ["projected_qty", "actual_qty"], as_dict=True) or {}
+
+def _get_customer_item_code(args, item_bean):
+ customer_item_code = item_bean.doclist.get({"parentfield": "item_customer_details",
+ "customer_name": args.customer})
+
+ return customer_item_code and customer_item_code[0].ref_code or None
+
\ No newline at end of file
diff --git a/setup/utils.py b/setup/utils.py
index 1a86921..33fa3e2 100644
--- a/setup/utils.py
+++ b/setup/utils.py
@@ -46,4 +46,4 @@
if result and len(result)==1:
return {"price_list_currency": result[0][0]}
else:
- return {}
\ No newline at end of file
+ return {}
diff --git a/startup/boot.py b/startup/boot.py
index 9ed20ff..b202d17 100644
--- a/startup/boot.py
+++ b/startup/boot.py
@@ -36,9 +36,8 @@
for key in ['max_users', 'expires_on', 'max_space', 'status', 'developer_mode']:
if hasattr(conf, key): bootinfo[key] = getattr(conf, key)
- bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center,
- cost_center as 'cost_center_other_charges' from `tabCompany`""",
- as_dict=1, update={"doctype":":Company"})
+ bootinfo['docs'] += webnotes.conn.sql("""select name, default_currency, cost_center
+ from `tabCompany`""", as_dict=1, update={"doctype":":Company"})
def get_letter_heads():
"""load letter heads with startup"""
diff --git a/stock/doctype/delivery_note/delivery_note.txt b/stock/doctype/delivery_note/delivery_note.txt
index 36c2789..6f299ef 100644
--- a/stock/doctype/delivery_note/delivery_note.txt
+++ b/stock/doctype/delivery_note/delivery_note.txt
@@ -1,8 +1,8 @@
[
{
- "creation": "2013-04-02 10:50:50",
+ "creation": "2013-05-06 12:03:30",
"docstatus": 0,
- "modified": "2013-02-02 19:18:38",
+ "modified": "2013-05-06 13:08:13",
"modified_by": "Administrator",
"owner": "Administrator"
},
@@ -255,7 +255,7 @@
"oldfieldname": "net_total",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
- "print_hide": 0,
+ "print_hide": 1,
"print_width": "150px",
"read_only": 1,
"reqd": 0,
@@ -263,6 +263,14 @@
},
{
"doctype": "DocField",
+ "fieldname": "net_total_export",
+ "fieldtype": "Currency",
+ "label": "Net Total (Export)",
+ "options": "currency",
+ "read_only": 1
+ },
+ {
+ "doctype": "DocField",
"fieldname": "recalculate_values",
"fieldtype": "Button",
"label": "Re-Calculate Values",
@@ -1136,7 +1144,6 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "match": "",
"permlevel": 1,
"report": 0,
"role": "Material User",
@@ -1159,7 +1166,6 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "match": "",
"permlevel": 1,
"report": 0,
"role": "Material Manager",
@@ -1171,7 +1177,6 @@
"cancel": 1,
"create": 1,
"doctype": "DocPerm",
- "match": "",
"permlevel": 0,
"report": 1,
"role": "Sales User",
@@ -1183,7 +1188,6 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "match": "",
"permlevel": 1,
"report": 0,
"role": "Sales User",
@@ -1205,7 +1209,6 @@
"cancel": 0,
"create": 0,
"doctype": "DocPerm",
- "match": "",
"permlevel": 1,
"role": "Accounts User",
"submit": 0
diff --git a/stock/doctype/item/item.txt b/stock/doctype/item/item.txt
index c799029..5f6c760 100644
--- a/stock/doctype/item/item.txt
+++ b/stock/doctype/item/item.txt
@@ -2,7 +2,7 @@
{
"creation": "2013-05-03 10:45:46",
"docstatus": 0,
- "modified": "2013-05-07 15:58:58",
+ "modified": "2013-05-07 16:00:00",
"modified_by": "Administrator",
"owner": "Administrator"
},
diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py
index c3ce2d7..a9281cd 100644
--- a/stock/doctype/stock_entry/test_stock_entry.py
+++ b/stock/doctype/stock_entry/test_stock_entry.py
@@ -450,6 +450,7 @@
for d in pi.doclist.get({"parentfield": "entries"}):
d.expense_head = "_Test Account Cost for Goods Sold - _TC"
d.cost_center = "_Test Cost Center - _TC"
+
for d in pi.doclist.get({"parentfield": "purchase_tax_details"}):
d.cost_center = "_Test Cost Center - _TC"
diff --git a/utilities/transaction_base.py b/utilities/transaction_base.py
index 5d7d1a8..540b385 100644
--- a/utilities/transaction_base.py
+++ b/utilities/transaction_base.py
@@ -16,6 +16,7 @@
from __future__ import unicode_literals
import webnotes
+from webnotes import msgprint, _
from webnotes.utils import load_json, cstr, flt, now_datetime
from webnotes.model.doc import addchild
@@ -268,4 +269,42 @@
def validate_posting_time(self):
if not self.doc.posting_time:
self.doc.posting_time = now_datetime().strftime('%H:%M:%S')
-
\ No newline at end of file
+
+def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company):
+ """common validation for currency and price list currency"""
+ if conversion_rate == 0:
+ msgprint(conversion_rate_label + _(' cannot be 0'), raise_exception=True)
+
+ company_currency = webnotes.conn.get_value("Company", company, "default_currency")
+
+ # parenthesis for 'OR' are necessary as we want it to evaluate as
+ # mandatory valid condition and (1st optional valid condition
+ # or 2nd optional valid condition)
+ valid_conversion_rate = (conversion_rate and
+ ((currency == company_currency and conversion_rate == 1.00)
+ or (currency != company_currency and conversion_rate != 1.00)))
+
+ if not valid_conversion_rate:
+ msgprint(_('Please enter valid ') + conversion_rate_label + (': ')
+ + ("1 %s = [?] %s" % (currency, company_currency)),
+ raise_exception=True)
+
+def validate_item_fetch(args, item):
+ from stock.utils import validate_end_of_life
+ validate_end_of_life(item.name, item.end_of_life)
+
+ # validate company
+ if not args.company:
+ msgprint(_("Please specify Company"), raise_exception=True)
+
+ # validate conversion rates
+ meta = webnotes.get_doctype(args.doctype)
+ if meta.get_field("currency"):
+ # validate conversion rate
+ validate_conversion_rate(args.currency, args.conversion_rate,
+ meta.get_label("conversion_rate"), args.company)
+
+ # validate price list conversion rate
+ if args.price_list_name and args.price_list_currency:
+ validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
+ meta.get_label("plc_conversion_rate"), args.company)
\ No newline at end of file