diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py index 1c0c31a7567..90647547fd3 100644 --- a/erpnext/accounts/doctype/c_form/c_form.py +++ b/erpnext/accounts/doctype/c_form/c_form.py @@ -18,17 +18,17 @@ class CForm(Document): `tabSales Invoice` where name = %s and docstatus = 1""", d.invoice_no) if inv and inv[0][0] != 'Yes': - frappe.throw("C-form is not applicable for Invoice: %s" % d.invoice_no) + frappe.throw("C-form is not applicable for Invoice: {0}".format(d.invoice_no)) elif inv and inv[0][1] and inv[0][1] != self.name: - frappe.throw("""Invoice %s is tagged in another C-form: %s. + frappe.throw("""Invoice {0} is tagged in another C-form: {1}. If you want to change C-form no for this invoice, - please remove invoice no from the previous c-form and then try again""" % - (d.invoice_no, inv[0][1])) + please remove invoice no from the previous c-form and then try again"""\ + .format(d.invoice_no, inv[0][1])) elif not inv: - frappe.throw("Row %s: Invoice %s is invalid, it might be cancelled / does not exist. \ - Please enter a valid Invoice" % d.idx, d.invoice_no) + frappe.throw("Row {0}: Invoice {1} is invalid, it might be cancelled / does not exist. \ + Please enter a valid Invoice".format(d.idx, d.invoice_no)) def on_update(self): """ Update C-Form No on invoices""" diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index e3a164deaa7..0e7a640f9ad 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -399,4 +399,4 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): and tabAccount.company = '%(company)s' and tabAccount.%(key)s LIKE '%(txt)s' %(mcond)s""" % {'company': filters['company'], 'key': searchfield, - 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype)}) + 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype)}) \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 607995b1dab..b3b35f9745e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -230,7 +230,7 @@ cur_frm.cscript.hide_fields = function(doc) { cur_frm.fields_dict['items'].grid.set_column_disp(item_flds_normal, true); } - item_flds_stock = ['serial_no', 'batch_no', 'actual_qty', 'expense_account', 'warehouse'] + item_flds_stock = ['serial_no', 'batch_no', 'actual_qty', 'expense_account', 'warehouse', 'expense_account', 'warehouse'] cur_frm.fields_dict['items'].grid.set_column_disp(item_flds_stock, (cint(doc.update_stock)==1 ? true : false)); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index b546963f742..9cb63b2e5e4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -613,7 +613,6 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters): %(mcond)s""" % {'company': filters['company'], 'key': searchfield, 'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype)}) - @frappe.whitelist() def make_delivery_note(source_name, target_doc=None): def set_missing_values(source, target): diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index e720f1b02b3..feb6bb8bab8 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -391,6 +391,19 @@ "fieldtype": "Column Break", "permlevel": 0 }, + { + "allow_on_submit": 1, + "fieldname": "actual_batch_qty", + "fieldtype": "Float", + "label": "Available Batch Qty at Warehouse", + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, + "width": "150px" + }, { "allow_on_submit": 1, "fieldname": "actual_qty", @@ -491,7 +504,7 @@ ], "idx": 1, "istable": 1, - "modified": "2015-02-23 15:55:23.143072", + "modified": "2015-03-10 14:56:45.641026", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 2dbdb6abd21..6ba2be88327 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -119,6 +119,8 @@ class AccountsController(TransactionBase): if item.get("item_code"): args = parent_dict.copy() args.update(item.as_dict()) + if not args.get("transaction_date"): + args["transaction_date"] = args.get("posting_date") ret = get_item_details(args) for fieldname, value in ret.items(): @@ -165,7 +167,6 @@ class AccountsController(TransactionBase): if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"): frappe.throw(_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges)) - def get_gl_dict(self, args): """this method populates the common properties of a gl entry record""" gl_dict = frappe._dict({ diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 3d6ba1913c3..4c4f2816370 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -31,6 +31,7 @@ class calculate_taxes_and_totals(object): self.determine_exclusive_rate() self.calculate_net_total() self.calculate_taxes() + self.manipulate_grand_total_for_inclusive_tax() self.calculate_totals() self._cleanup() @@ -281,6 +282,21 @@ class calculate_taxes_and_totals(object): tax.total = flt(tax.total + discount_amount_loss, tax.precision("total")) self._set_in_company_currency(tax, ["total", "tax_amount_after_discount_amount"]) + + def manipulate_grand_total_for_inclusive_tax(self): + # if fully inclusive taxes and diff + if self.doc.get("taxes") and all(cint(t.included_in_print_rate) for t in self.doc.get("taxes")): + + last_tax = self.doc.get("taxes")[-1] + + diff = self.doc.net_total - flt(last_tax.total / self.doc.conversion_rate, + self.precision("grand_total_export")) + + if diff and abs(diff) <= (2.0 / 10**last_tax.precision("tax_amount")): + adjustment_amount = flt(diff * self.doc.conversion_rate, last_tax.precision("tax_amount")) + last_tax.tax_amount += adjustment_amount + last_tax.tax_amount_after_discount_amount += adjustment_amount + last_tax.total += adjustment_amount def calculate_totals(self): self.doc.grand_total = flt(self.doc.get("taxes")[-1].total diff --git a/erpnext/patches.txt b/erpnext/patches.txt index de9f26b4eea..1b78b10b62e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -131,3 +131,5 @@ erpnext.patches.v5_0.update_account_types erpnext.patches.v5_0.update_sms_sender erpnext.patches.v5_0.set_appraisal_remarks erpnext.patches.v5_0.update_time_log_title +erpnext.patches.v4_2.repost_reserved_qty +erpnext.patches.v4_2.repost_sle_for_si_with_no_warehouse diff --git a/erpnext/patches/v4_2/repost_reserved_qty.py b/erpnext/patches/v4_2/repost_reserved_qty.py new file mode 100644 index 00000000000..04dfb77bfa2 --- /dev/null +++ b/erpnext/patches/v4_2/repost_reserved_qty.py @@ -0,0 +1,12 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.utilities.repost_stock import update_bin_qty, get_reserved_qty + +def execute(): + for item_code, warehouse in frappe.db.sql("select item_code, warehouse from tabBin where ifnull(reserved_qty, 0) < 0"): + update_bin_qty(item_code, warehouse, { + "reserved_qty": get_reserved_qty(item_code, warehouse) + }) \ No newline at end of file diff --git a/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py b/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py new file mode 100644 index 00000000000..44bec0091a6 --- /dev/null +++ b/erpnext/patches/v4_2/repost_sle_for_si_with_no_warehouse.py @@ -0,0 +1,34 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.stock.stock_ledger import NegativeStockError + +def execute(): + si_list = frappe.db.sql("""select distinct si.name + from `tabSales Invoice Item` si_item, `tabSales Invoice` si + where si.name = si_item.parent and si.modified > '2015-02-16' and si.docstatus=1 + and ifnull(si_item.warehouse, '') = '' and ifnull(si.update_stock, 0) = 1 + order by posting_date, posting_time""", as_dict=1) + + failed_list = [] + for si in si_list: + try: + si_doc = frappe.get_doc("Sales Invoice", si.name) + si_doc.docstatus = 2 + si_doc.on_cancel() + + si_doc.docstatus = 1 + si_doc.set_missing_item_details() + si_doc.on_submit() + frappe.db.commit() + except: + failed_list.append(si.name) + frappe.local.stockledger_exceptions = None + frappe.db.rollback() + + print "Failed to repost: ", failed_list + + + \ No newline at end of file diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index e41a271f805..eb22bf33b3f 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -33,6 +33,7 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({ this.determine_exclusive_rate(); this.calculate_net_total(); this.calculate_taxes(); + this.manipulate_grand_total_for_inclusive_tax(); this.calculate_totals(); this._cleanup(); this.show_item_wise_taxes(); @@ -319,6 +320,31 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({ discount_amount_loss, precision("tax_amount", tax)); tax.total = flt(tax.total + discount_amount_loss, precision("total", tax)); }, + + manipulate_grand_total_for_inclusive_tax: function() { + var me = this; + // if fully inclusive taxes and diff + if (this.frm.doc["taxes"].length) { + var all_inclusive = frappe.utils.all(this.frm.doc["taxes"].map(function(d) { + return cint(d.included_in_print_rate); + })); + + if (all_inclusive) { + var last_tax = me.frm.doc["taxes"].slice(-1)[0]; + + var diff = me.frm.doc.net_total + - flt(last_tax.total / me.frm.doc.conversion_rate, precision("grand_total")); + + if ( diff && Math.abs(diff) <= (2.0 / Math.pow(10, last_tax.precision("tax_amount"))) ) { + var adjustment_amount = flt(diff * me.frm.doc.conversion_rate, + last_tax.precision("tax_amount")); + last_tax.tax_amount += adjustment_amount; + last_tax.tax_amount_after_discount += adjustment_amount; + last_tax.total += adjustment_amount; + } + } + } + }, calculate_totals: function() { // Changing sequence can cause roundiing issue and on-screen discrepency diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 8ffc110768e..7ee342f685d 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -109,7 +109,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ order_type: me.frm.doc.order_type, is_pos: cint(me.frm.doc.is_pos), is_subcontracted: me.frm.doc.is_subcontracted, - transaction_date: me.frm.doc.transaction_date, + transaction_date: me.frm.doc.transaction_date || me.frm.doc.posting_date, ignore_pricing_rule: me.frm.doc.ignore_pricing_rule, doctype: item.doctype, name: item.name, diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 271b44a6b9c..486b898999f 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -190,6 +190,8 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }, warehouse: function(doc, cdt, cdn) { + var me = this; + this.batch_no(doc, cdt, cdn); var item = frappe.get_doc(cdt, cdn); if(item.item_code && item.warehouse) { return this.frm.call({ @@ -277,6 +279,21 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ } }, + batch_no: function(doc, cdt, cdn) { + var me = this; + var item = frappe.get_doc(cdt, cdn); + return this.frm.call({ + method: "erpnext.stock.get_item_details.get_batch_qty", + child: item, + args: { + "batch_no": item.batch_no, + "warehouse": item.warehouse, + "item_code": item.item_code + }, + "fieldname": "actual_batch_qty" + }); + }, + set_dynamic_labels: function() { this._super(); this.set_sales_bom_help(this.frm.doc); diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index adcf258a4cd..66b6f19803f 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -395,6 +395,19 @@ "fieldtype": "Column Break", "permlevel": 0 }, + { + "allow_on_submit": 1, + "fieldname": "actual_batch_qty", + "fieldtype": "Float", + "label": "Available Batch Qty at Warehouse", + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, + "width": "150px" + }, { "allow_on_submit": 1, "fieldname": "actual_qty", @@ -508,7 +521,7 @@ ], "idx": 1, "istable": 1, - "modified": "2015-02-23 15:51:20.772564", + "modified": "2015-03-10 12:21:17.028911", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 0613d3e6f85..87465a8509d 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -304,6 +304,15 @@ def get_serial_nos_by_fifo(args, item_doc): "qty": cint(args.qty) })) +def get_actual_batch_qty(batch_no,warehouse,item_code): + actual_batch_qty = 0 + if batch_no: + actual_batch_qty = flt(frappe.db.sql("""select sum(actual_qty) + from `tabStock Ledger Entry` + where warehouse=%s and item_code=%s and batch_no=%s""", + (warehouse, item_code, batch_no))[0][0]) + return actual_batch_qty + @frappe.whitelist() def get_conversion_factor(item_code, uom): variant_of = frappe.db.get_value("Item", item_code, "variant_of") @@ -324,6 +333,12 @@ def get_available_qty(item_code, warehouse): return frappe.db.get_value("Bin", {"item_code": item_code, "warehouse": warehouse}, ["projected_qty", "actual_qty"], as_dict=True) or {} +@frappe.whitelist() +def get_batch_qty(batch_no,warehouse,item_code): + actual_batch_qty = get_actual_batch_qty(batch_no,warehouse,item_code) + if batch_no: + return {'actual_batch_qty': actual_batch_qty} + @frappe.whitelist() def apply_price_list(args): """