[taxes and charges] refactor

This commit is contained in:
Rushabh Mehta 2015-02-25 15:08:42 +05:30
parent e91388381c
commit 2a21bc9fc2
15 changed files with 213 additions and 136 deletions

View File

@ -1,6 +1,8 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.tax_table = "Purchase Taxes and Charges";
{% include "public/js/controllers/accounts.js" %}
frappe.ui.form.on("Purchase Taxes and Charges", "add_deduct_tax", function(doc, cdt, cdn) {

View File

@ -199,8 +199,8 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
this.hide_fields(this.frm.doc);
},
items_on_form_rendered: function(doc, grid_row) {
erpnext.setup_serial_no(grid_row)
items_on_form_rendered: function() {
erpnext.setup_serial_no();
}
});

View File

@ -56,7 +56,7 @@
"oldfieldname": "rate",
"oldfieldtype": "Currency",
"permlevel": 0,
"reqd": 1
"reqd": 0
},
{
"fieldname": "col_break_1",
@ -186,7 +186,7 @@
"hide_heading": 1,
"idx": 1,
"istable": 1,
"modified": "2015-02-23 12:36:02.213508",
"modified": "2015-02-25 02:50:44.152307",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Taxes and Charges",

View File

@ -1,9 +1,10 @@
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.tax_table = "Sales Taxes and Charges";
{% include "public/js/controllers/accounts.js" %}
cur_frm.cscript.onload = function(doc, cdt, cdn) {
if(doc.doctype === "Sales Taxes and Charges Master")
erpnext.add_applicable_territory();
}
frappe.ui.form.on("Sales Taxes and Charges Master", "onload", function(frm) {
erpnext.add_applicable_territory();
});

View File

@ -20,3 +20,4 @@ class SalesTaxesandChargesMaster(Document):
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, self)

View File

@ -370,6 +370,15 @@ def validate_taxes_and_charges(tax):
elif tax.row_id and cint(tax.row_id) >= cint(tax.idx):
frappe.throw(_("Cannot refer row number greater than or equal to current row number for this Charge type"))
if tax.charge_type == "Actual":
if not tax.tax_amount:
frappe.throw(_("Amount is mandatory for charge type 'Actual'"))
tax.rate = None
else:
if not tax.rate:
frappe.throw(_("Rate is mandatory for charge type '{0}'").format(tax.charge_type))
tax.tax_amount = None
def validate_inclusive_tax(tax, doc):
def _on_previous_row_error(row_range):
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx,

View File

@ -126,3 +126,4 @@ erpnext.patches.v5_0.item_patches
erpnext.patches.v5_0.update_journal_entry_title
erpnext.patches.v5_0.taxes_and_totals_in_party_currency
erpnext.patches.v5_0.replace_renamed_fields_in_custom_scripts_and_print_formats
execute:frappe.db.sql("update `tabStock Entry` set from_bom = if(ifnull(bom_no, '')='', 0, 1)")

View File

@ -2,6 +2,10 @@
// License: GNU General Public License v3. See license.txt
// get tax rate
frappe.provide("erpnext.taxes");
frappe.provide("erpnext.taxes.flags");
cur_frm.cscript.account_head = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(!d.charge_type && d.account_head){
@ -82,34 +86,60 @@ cur_frm.cscript.validate_inclusive_tax = function(tax) {
}
}
frappe.ui.form.on(cur_frm.cscript.tax_table, "row_id", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
});
if(!erpnext.taxes.flags[cur_frm.cscript.tax_table]) {
erpnext.taxes.flags[cur_frm.cscript.tax_table] = true;
frappe.ui.form.on(cur_frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
});
frappe.ui.form.on(cur_frm.cscript.tax_table, "tax_amount", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
});
frappe.ui.form.on(cur_frm.cscript.tax_table, "charge_type", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
});
frappe.ui.form.on(cur_frm.cscript.tax_table, "included_in_print_rate", function(frm, cdt, cdn) {
var tax = frappe.get_doc(cdt, cdn);
try {
frappe.ui.form.on(cur_frm.cscript.tax_table, "row_id", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
cur_frm.cscript.validate_inclusive_tax(tax);
} catch(e) {
tax.included_in_print_rate = 0;
refresh_field("included_in_print_rate", tax.name, tax.parentfield);
throw e;
});
frappe.ui.form.on(cur_frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
});
frappe.ui.form.on(cur_frm.cscript.tax_table, "tax_amount", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
});
frappe.ui.form.on(cur_frm.cscript.tax_table, "charge_type", function(frm, cdt, cdn) {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm);
});
frappe.ui.form.on(cur_frm.cscript.tax_table, "included_in_print_rate", function(frm, cdt, cdn) {
var tax = frappe.get_doc(cdt, cdn);
try {
cur_frm.cscript.validate_taxes_and_charges(cdt, cdn);
cur_frm.cscript.validate_inclusive_tax(tax);
} catch(e) {
tax.included_in_print_rate = 0;
refresh_field("included_in_print_rate", tax.name, tax.parentfield);
throw e;
}
});
}
erpnext.taxes.set_conditional_mandatory_rate_or_amount = function(frm) {
var grid_row = frm.open_grid_row();
if(grid_row.doc.charge_type==="Actual") {
grid_row.toggle_display("tax_amount", true);
grid_row.toggle_reqd("tax_amount", true);
grid_row.toggle_display("rate", false);
grid_row.toggle_reqd("rate", false);
} else {
grid_row.toggle_display("rate", true);
grid_row.toggle_reqd("rate", true);
grid_row.toggle_display("tax_amount", false);
grid_row.toggle_reqd("tax_amount", false);
}
}
// setup conditional mandatory for tax and rates
frappe.ui.form.on(cur_frm.doctype, "taxes_on_form_rendered", function(frm) {
erpnext.taxes.set_conditional_mandatory_rate_or_amount(frm);
});
cur_frm.set_query("account_head", "taxes", function(doc) {
if(cur_frm.cscript.tax_table == "Sales Taxes and Charges") {
var account_type = ["Tax", "Chargeable", "Expense Account"];

View File

@ -57,13 +57,15 @@ $.extend(erpnext, {
}
},
setup_serial_no: function(grid_row) {
setup_serial_no: function() {
var grid_row = cur_frm.open_grid_row();
if(!grid_row.fields_dict.serial_no ||
grid_row.fields_dict.serial_no.get_status()!=="Write") return;
var $btn = $('<button class="btn btn-sm btn-default">'+__("Add Serial No")+'</button>')
.appendTo($("<div>")
.css({"margin-bottom": "10px", "margin-left": "15px"})
.css({"margin-bottom": "10px", "margin-top": "10px"})
.appendTo(grid_row.fields_dict.serial_no.$wrapper));
$btn.on("click", function() {
@ -99,7 +101,7 @@ $.extend(erpnext, {
d.show();
});
},
get_letter_head: function(company) {
frappe.call({
type:"GET",
@ -112,7 +114,7 @@ $.extend(erpnext, {
}
});
},
});

View File

@ -79,7 +79,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
},
items_on_form_rendered: function(doc, grid_row) {
erpnext.setup_serial_no(grid_row)
erpnext.setup_serial_no();
}
});

View File

@ -274,7 +274,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
},
items_on_form_rendered: function(doc, grid_row) {
erpnext.setup_serial_no(grid_row)
erpnext.setup_serial_no();
},
customer: function() {

View File

@ -7,6 +7,14 @@
"docstatus": 0,
"doctype": "DocType",
"fields": [
{
"fieldname": "items_section",
"fieldtype": "Section Break",
"label": "",
"oldfieldtype": "Section Break",
"permlevel": 0,
"read_only": 0
},
{
"fieldname": "col1",
"fieldtype": "Column Break",
@ -73,15 +81,6 @@
"reqd": 0,
"search_index": 1
},
{
"depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"fieldname": "bom_no",
"fieldtype": "Link",
"label": "BOM No",
"options": "BOM",
"permlevel": 0,
"read_only": 0
},
{
"allow_on_submit": 0,
"depends_on": "eval:doc.purpose==\"Sales Return\"",
@ -132,6 +131,13 @@
"reqd": 0,
"search_index": 1
},
{
"fieldname": "from_bom",
"fieldtype": "Check",
"label": "From BOM",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "col2",
"fieldtype": "Column Break",
@ -178,13 +184,91 @@
"search_index": 0
},
{
"fieldname": "items_section",
"depends_on": "eval: doc.from_bom && (doc.purpose!==\"Sales Return\" && doc.purpose!==\"Purchase Return\")",
"fieldname": "sb1",
"fieldtype": "Section Break",
"label": "",
"oldfieldtype": "Section Break",
"permlevel": 0,
"read_only": 0
},
{
"depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"fieldname": "bom_no",
"fieldtype": "Link",
"label": "BOM No",
"options": "BOM",
"permlevel": 0,
"read_only": 0
},
{
"depends_on": "eval:inList([\"Manufacture\", \"Repack\"], doc.purpose)",
"fieldname": "additional_operating_cost",
"fieldtype": "Currency",
"label": "Additional Operating Cost",
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"read_only": 0
},
{
"fieldname": "cb1",
"fieldtype": "Column Break",
"permlevel": 0,
"read_only": 0
},
{
"allow_on_submit": 0,
"depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"description": "As per Stock UOM",
"fieldname": "fg_completed_qty",
"fieldtype": "Float",
"hidden": 0,
"in_filter": 0,
"label": "Manufacturing Quantity",
"no_copy": 0,
"oldfieldname": "fg_completed_qty",
"oldfieldtype": "Currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0
},
{
"default": "1",
"depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"description": "Including items for sub assemblies",
"fieldname": "use_multi_level_bom",
"fieldtype": "Check",
"label": "Use Multi-Level BOM",
"permlevel": 0,
"print_hide": 1,
"read_only": 0
},
{
"allow_on_submit": 0,
"depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"fieldname": "get_items",
"fieldtype": "Button",
"hidden": 0,
"in_filter": 0,
"label": "Get Items",
"no_copy": 0,
"oldfieldtype": "Button",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0
},
{
"fieldname": "section_break_12",
"fieldtype": "Section Break",
"permlevel": 0,
"precision": ""
},
{
"allow_on_submit": 0,
"fieldname": "from_warehouse",
@ -301,77 +385,6 @@
"precision": "",
"read_only": 1
},
{
"depends_on": "eval:(doc.purpose!==\"Sales Return\" && doc.purpose!==\"Purchase Return\")",
"fieldname": "sb1",
"fieldtype": "Section Break",
"label": "From Bill of Materials",
"permlevel": 0,
"read_only": 0
},
{
"allow_on_submit": 0,
"depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"description": "As per Stock UOM",
"fieldname": "fg_completed_qty",
"fieldtype": "Float",
"hidden": 0,
"in_filter": 0,
"label": "Manufacturing Quantity",
"no_copy": 0,
"oldfieldname": "fg_completed_qty",
"oldfieldtype": "Currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0
},
{
"depends_on": "eval:inList([\"Manufacture\", \"Repack\"], doc.purpose)",
"fieldname": "additional_operating_cost",
"fieldtype": "Currency",
"label": "Additional Operating Cost",
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"read_only": 0
},
{
"fieldname": "cb1",
"fieldtype": "Column Break",
"permlevel": 0,
"read_only": 0
},
{
"default": "1",
"depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.",
"fieldname": "use_multi_level_bom",
"fieldtype": "Check",
"label": "Use Multi-Level BOM",
"permlevel": 0,
"print_hide": 1,
"read_only": 0
},
{
"allow_on_submit": 0,
"depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"fieldname": "get_items",
"fieldtype": "Button",
"hidden": 0,
"in_filter": 0,
"label": "Get Items",
"no_copy": 0,
"oldfieldtype": "Button",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0
},
{
"fieldname": "fold",
"fieldtype": "Fold",
@ -632,7 +645,7 @@
"is_submittable": 1,
"issingle": 0,
"max_attachments": 0,
"modified": "2015-02-20 05:04:09.060180",
"modified": "2015-02-25 01:59:14.371042",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",

View File

@ -282,14 +282,15 @@ class StockEntry(StockController):
def add_operation_cost(self, raw_material_cost, force):
"""Adds operating cost if Production Order is set"""
# set incoming rate for fg item
if self.production_order:
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
for d in self.get("items"):
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
number_of_fg_items = len([t.t_warehouse for t in self.get("items") if t.t_warehouse])
for d in self.get("items"):
if (d.t_warehouse and number_of_fg_items == 1):
operation_cost_per_unit = 0.0
if self.production_order:
operation_cost_per_unit = self.get_operation_cost_per_unit(d.bom_no, d.qty)
d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty))
d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), self.precision("transfer_qty", d))
break
d.incoming_rate = operation_cost_per_unit + (raw_material_cost / flt(d.transfer_qty))
d.amount = flt(flt(d.transfer_qty) * flt(d.incoming_rate), self.precision("transfer_qty", d))
break
def get_operation_cost_per_unit(self, bom_no, qty):
"""Returns operating cost from Production Order for given `bom_no`"""
@ -510,10 +511,14 @@ class StockEntry(StockController):
self.set('items', [])
self.validate_production_order()
if not getattr(self, "pro_doc", None):
self.pro_doc = None
if self.production_order:
# common validations
if not getattr(self, "pro_doc", None):
if not self.pro_doc:
self.pro_doc = frappe.get_doc('Production Order', self.production_order)
if self.pro_doc:
self.bom_no = self.pro_doc.bom_no
else:

View File

@ -10,12 +10,18 @@ from erpnext.stock.stock_ledger import update_entries_after
from erpnext.controllers.stock_controller import StockController
from erpnext.stock.utils import get_stock_balance
class OpeningEntryAccountError(frappe.ValidationError): pass
class StockReconciliation(StockController):
def __init__(self, arg1, arg2=None):
super(StockReconciliation, self).__init__(arg1, arg2)
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
def validate(self):
if not self.expense_account:
self.expense_account = frappe.db.get_value("Company", self.company, "stock_adjustment_account")
if not self.cost_center:
self.cost_center = frappe.db.get_value("Company", self.company, "cost_center")
self.validate_posting_time()
self.remove_items_with_no_change()
self.validate_data()
@ -215,7 +221,12 @@ class StockReconciliation(StockController):
msgprint(_("Please enter Expense Account"), raise_exception=1)
elif not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"))
frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"), OpeningEntryAccountError)
def get_items_for(self, warehouse):
self.items = []
for item in get_items(warehouse, self.posting_date, self.posting_time):
self.append("items", item)
@frappe.whitelist()
def get_items(warehouse, posting_date, posting_time):

View File

@ -150,7 +150,7 @@ class update_entries_after(object):
return
if sle.serial_no:
self.valuation_rate = self.get_serialized_values(sle)
self.get_serialized_values(sle)
self.qty_after_transaction += flt(sle.actual_qty)
self.stock_value = flt(self.qty_after_transaction) * flt(self.valuation_rate)
else:
@ -208,12 +208,14 @@ class update_entries_after(object):
if incoming_rate < 0:
# wrong incoming rate
incoming_rate = self.valuation_rate
elif incoming_rate == 0 or flt(sle.actual_qty) < 0:
# In case of delivery/stock issue, get average purchase rate
# of serial nos of current entry
incoming_rate = flt(frappe.db.sql("""select avg(ifnull(purchase_rate, 0))
from `tabSerial No` where name in (%s)""" % (", ".join(["%s"]*len(serial_no))),
tuple(serial_no))[0][0])
elif incoming_rate == 0:
if flt(sle.actual_qty) < 0:
# In case of delivery/stock issue, get average purchase rate
# of serial nos of current entry
incoming_rate = flt(frappe.db.sql("""select avg(ifnull(purchase_rate, 0))
from `tabSerial No` where name in (%s)""" % (", ".join(["%s"]*len(serial_no))),
tuple(serial_no))[0][0])
if incoming_rate and not self.valuation_rate:
self.valuation_rate = incoming_rate