Ported v3 fixes to v4
Commit range: webnotes/erpnext@e4b72df2f4bd7272c42e90f6faec3c27aa763b82..webnotes/erpnext@85441b0180f84246259d5a0ab4e3bcbf29355efe
This commit is contained in:
parent
13429a3b34
commit
652bc0784a
@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt, cstr, cint
|
||||
from frappe.utils import flt, cstr, cint, getdate, add_days, formatdate
|
||||
from frappe import msgprint, throw, _
|
||||
from frappe.model.document import Document
|
||||
|
||||
@ -151,6 +151,27 @@ class Account(Document):
|
||||
and not self.get_authorized_user():
|
||||
throw(_("{0} Credit limit {0} crossed").format(_(credit_limit_from), credit_limit))
|
||||
|
||||
def validate_due_date(self, posting_date, due_date):
|
||||
credit_days = (self.credit_days or frappe.db.get_value("Company", self.company, "credit_days"))
|
||||
if credit_days is None:
|
||||
return
|
||||
|
||||
posting_date, due_date = getdate(posting_date), getdate(due_date)
|
||||
diff = (due_date - posting_date).days
|
||||
|
||||
if diff < 0:
|
||||
frappe.throw(_("Due Date cannot be before Posting Date"))
|
||||
elif diff > credit_days:
|
||||
is_credit_controller = frappe.db.get_value("Accounts Settings", None,
|
||||
"credit_controller") in frappe.user.get_roles()
|
||||
|
||||
if is_credit_controller:
|
||||
msgprint(_("Note: Due Date exceeds the allowed credit days by {0} day(s)").format(
|
||||
diff - credit_days))
|
||||
else:
|
||||
max_due_date = formatdate(add_days(posting_date, credit_days))
|
||||
frappe.throw(_("Due Date cannot be after {0}").format(max_due_date))
|
||||
|
||||
def validate_trash(self):
|
||||
"""checks gl entries and if child exists"""
|
||||
if not self.parent_account:
|
||||
|
@ -25,8 +25,7 @@ class GLEntry(Document):
|
||||
validate_balance_type(self.account, adv_adj)
|
||||
|
||||
# Update outstanding amt on against voucher
|
||||
if self.against_voucher and self.against_voucher_type != "POS" \
|
||||
and update_outstanding == 'Yes':
|
||||
if self.against_voucher and update_outstanding == 'Yes':
|
||||
update_outstanding_amt(self.account, self.against_voucher_type,
|
||||
self.against_voucher)
|
||||
|
||||
|
@ -212,7 +212,7 @@ class JournalVoucher(AccountsController):
|
||||
self.is_approving_authority = 0
|
||||
|
||||
# Fetch credit controller role
|
||||
approving_authority = frappe.db.get_value("Global Defaults", None,
|
||||
approving_authority = frappe.db.get_value("Accounts Settings", None,
|
||||
"credit_controller")
|
||||
|
||||
# Check logged-in user is authorized
|
||||
|
@ -50,6 +50,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.validate_with_previous_doc()
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
self.set_aging_date()
|
||||
frappe.get_doc("Account", self.credit_to).validate_due_date(self.posting_date, self.due_date)
|
||||
self.set_against_expense_account()
|
||||
self.validate_write_off_account()
|
||||
self.update_valuation_rate("entries")
|
||||
|
@ -138,7 +138,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
wrapper.load_from_db()
|
||||
|
||||
expected_values = [
|
||||
["_Test FG Item", 90, 7059],
|
||||
["_Test FG Item", 90, 59],
|
||||
["_Test Item Home Desktop 200", 135, 177]
|
||||
]
|
||||
for i, item in enumerate(wrapper.get("entries")):
|
||||
|
@ -186,7 +186,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
precision("write_off_amount"));
|
||||
}
|
||||
|
||||
this.calculate_outstanding_amount();
|
||||
this.calculate_outstanding_amount(false);
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
|
||||
|
@ -65,6 +65,7 @@ class SalesInvoice(SellingController):
|
||||
self.is_opening = 'No'
|
||||
|
||||
self.set_aging_date()
|
||||
frappe.get_doc("Account", self.debit_to).validate_due_date(self.posting_date, self.due_date)
|
||||
self.set_against_income_account()
|
||||
self.validate_c_form()
|
||||
self.validate_time_logs_are_submitted()
|
||||
@ -464,6 +465,10 @@ class SalesInvoice(SellingController):
|
||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
|
||||
update_outstanding=update_outstanding, merge_entries=False)
|
||||
|
||||
if update_outstanding == "No":
|
||||
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||
update_outstanding_amt(self.debit_to, self.doctype, self.name)
|
||||
|
||||
if repost_future_gle and cint(self.update_stock) \
|
||||
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
|
||||
items, warehouse_account = self.get_items_and_warehouse_accounts()
|
||||
|
@ -120,7 +120,6 @@ def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||
validate_balance_type(entry["account"], adv_adj)
|
||||
validate_expense_against_budget(entry)
|
||||
|
||||
if entry.get("against_voucher") and entry.get("against_voucher_type") != "POS" \
|
||||
and update_outstanding == 'Yes':
|
||||
if entry.get("against_voucher") and update_outstanding == 'Yes':
|
||||
update_outstanding_amt(entry["account"], entry.get("against_voucher_type"),
|
||||
entry.get("against_voucher"), on_cancel=True)
|
@ -36,10 +36,11 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
or employee_name like "%(txt)s")
|
||||
%(mcond)s
|
||||
order by
|
||||
case when name like "%(txt)s" then 0 else 1 end,
|
||||
case when employee_name like "%(txt)s" then 0 else 1 end,
|
||||
name
|
||||
if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999),
|
||||
if(locate("%(_txt)s", employee_name), locate("%(_txt)s", item_name), 99999),
|
||||
name, employee_name
|
||||
limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt,
|
||||
'_txt': txt.replace("%", ""),
|
||||
'mcond':get_match_cond(doctype), 'start': start, 'page_len': page_len})
|
||||
|
||||
# searches for leads which are not converted
|
||||
@ -52,11 +53,12 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
or company_name like "%(txt)s")
|
||||
%(mcond)s
|
||||
order by
|
||||
case when name like "%(txt)s" then 0 else 1 end,
|
||||
case when lead_name like "%(txt)s" then 0 else 1 end,
|
||||
case when company_name like "%(txt)s" then 0 else 1 end,
|
||||
lead_name asc
|
||||
if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999),
|
||||
if(locate("%(_txt)s", lead_name), locate("%(_txt)s", name), 99999),
|
||||
if(locate("%(_txt)s", company_name), locate("%(_txt)s", name), 99999),
|
||||
name, lead_name
|
||||
limit %(start)s, %(page_len)s""" % {'key': searchfield, 'txt': "%%%s%%" % txt,
|
||||
'_txt': txt.replace("%", ""),
|
||||
'mcond':get_match_cond(doctype), 'start': start, 'page_len': page_len})
|
||||
|
||||
# searches for customer
|
||||
@ -76,11 +78,12 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
or customer_name like "%(txt)s")
|
||||
%(mcond)s
|
||||
order by
|
||||
case when name like "%(txt)s" then 0 else 1 end,
|
||||
case when customer_name like "%(txt)s" then 0 else 1 end,
|
||||
if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999),
|
||||
if(locate("%(_txt)s", customer_name), locate("%(_txt)s", name), 99999),
|
||||
name, customer_name
|
||||
limit %(start)s, %(page_len)s""" % {'field': fields,'key': searchfield,
|
||||
'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype),
|
||||
'txt': "%%%s%%" % txt, '_txt': txt.replace("%", ""),
|
||||
'mcond':get_match_cond(doctype),
|
||||
'start': start, 'page_len': page_len})
|
||||
|
||||
# searches for supplier
|
||||
@ -98,11 +101,12 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
or supplier_name like "%(txt)s")
|
||||
%(mcond)s
|
||||
order by
|
||||
case when name like "%(txt)s" then 0 else 1 end,
|
||||
case when supplier_name like "%(txt)s" then 0 else 1 end,
|
||||
if(locate("%(_txt)s", name), locate("%(_txt)s", name), 99999),
|
||||
if(locate("%(_txt)s", supplier_name), locate("%(_txt)s", name), 99999),
|
||||
name, supplier_name
|
||||
limit %(start)s, %(page_len)s """ % {'field': fields,'key': searchfield,
|
||||
'txt': "%%%s%%" % txt, 'mcond':get_match_cond(doctype), 'start': start,
|
||||
'txt': "%%%s%%" % txt, '_txt': txt.replace("%", ""),
|
||||
'mcond':get_match_cond(doctype), 'start': start,
|
||||
'page_len': page_len})
|
||||
|
||||
def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
@ -141,12 +145,17 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
and (tabItem.`{key}` LIKE %(txt)s
|
||||
or tabItem.item_name LIKE %(txt)s)
|
||||
{fcond} {mcond}
|
||||
order by
|
||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||
if(locate(%(_txt)s, item_name), locate(%(_txt)s, item_name), 99999),
|
||||
name, item_name
|
||||
limit %(start)s, %(page_len)s """.format(key=searchfield,
|
||||
fcond=get_filters_cond(doctype, filters, conditions),
|
||||
mcond=get_match_cond(doctype)),
|
||||
{
|
||||
"today": nowdate(),
|
||||
"txt": "%%%s%%" % txt,
|
||||
"_txt": txt.replace("%", ""),
|
||||
"start": start,
|
||||
"page_len": page_len
|
||||
})
|
||||
|
@ -6,10 +6,10 @@ import frappe
|
||||
|
||||
from frappe.utils import cstr, flt, nowdate
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
class OverProductionError(frappe.ValidationError): pass
|
||||
|
||||
from frappe.model.document import Document
|
||||
class StockOverProductionError(frappe.ValidationError): pass
|
||||
|
||||
class ProductionOrder(Document):
|
||||
|
||||
@ -89,21 +89,41 @@ class ProductionOrder(Document):
|
||||
frappe.msgprint(_("Production Order status is {0}").format(status))
|
||||
|
||||
|
||||
def update_status(self, status):
|
||||
if status == 'Stopped':
|
||||
frappe.db.set(self, 'status', cstr(status))
|
||||
else:
|
||||
if flt(self.qty) == flt(self.produced_qty):
|
||||
frappe.db.set(self, 'status', 'Completed')
|
||||
if flt(self.qty) > flt(self.produced_qty):
|
||||
frappe.db.set(self, 'status', 'In Process')
|
||||
if flt(self.produced_qty) == 0:
|
||||
frappe.db.set(self, 'status', 'Submitted')
|
||||
def update_status(self, status=None):
|
||||
if not status:
|
||||
status = self.status
|
||||
|
||||
if status != 'Stopped':
|
||||
stock_entries = frappe._dict(frappe.db.sql("""select purpose, sum(fg_completed_qty)
|
||||
from `tabStock Entry` where production_order=%s and docstatus=1
|
||||
group by purpose""", self.name))
|
||||
|
||||
status = "Submitted"
|
||||
if stock_entries:
|
||||
status = "In Process"
|
||||
produced_qty = stock_entries.get("Manufacture/Repack")
|
||||
if flt(produced_qty) == flt(self.qty):
|
||||
status = "Completed"
|
||||
|
||||
if status != self.status:
|
||||
self.db_set("status", status)
|
||||
|
||||
def update_produced_qty(self):
|
||||
produced_qty = frappe.db.sql("""select sum(fg_completed_qty)
|
||||
from `tabStock Entry` where production_order=%s and docstatus=1
|
||||
and purpose='Manufacture/Repack'""", self.name)
|
||||
produced_qty = flt(produced_qty[0][0]) if produced_qty else 0
|
||||
|
||||
if produced_qty > self.qty:
|
||||
frappe.throw(_("Cannot manufacture more than the planned Quantity to Manufacture ({0}) in Production Order: {1}").format(self.qty, self.name), StockOverProductionError)
|
||||
|
||||
self.db_set("produced_qty", produced_qty)
|
||||
|
||||
def on_submit(self):
|
||||
if not self.wip_warehouse:
|
||||
frappe.throw(_("WIP Warehouse required before Submit"))
|
||||
frappe.throw(_("Work-in-Progress Warehouse is required before Submit"))
|
||||
if not self.fg_warehouse:
|
||||
frappe.throw(_("For Warehouse is required before Submit"))
|
||||
frappe.db.set(self,'status', 'Submitted')
|
||||
self.update_planned_qty(self.qty)
|
||||
|
||||
|
@ -49,7 +49,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
return pro_doc.name
|
||||
|
||||
def test_over_production(self):
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry import StockOverProductionError
|
||||
from erpnext.manufacturing.doctype.production_order.production_order import StockOverProductionError
|
||||
pro_order = self.test_planned_qty()
|
||||
|
||||
stock_entry = make_stock_entry(pro_order, "Manufacture/Repack")
|
||||
|
@ -24,8 +24,10 @@ $.extend(erpnext.queries, {
|
||||
return { query: "erpnext.controllers.queries.account_query" };
|
||||
},
|
||||
|
||||
item: function() {
|
||||
return { query: "erpnext.controllers.queries.item_query" };
|
||||
item: function(filters) {
|
||||
var args = { query: "erpnext.controllers.queries.item_query" };
|
||||
if(filters) args["filters"] = filters;
|
||||
return args;
|
||||
},
|
||||
|
||||
bom: function() {
|
||||
|
@ -189,7 +189,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
this.calculate_taxes_and_totals();
|
||||
this.calculate_taxes_and_totals(false);
|
||||
},
|
||||
|
||||
company: function() {
|
||||
@ -299,7 +299,8 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
||||
tax_rate: function(doc, cdt, cdn) {
|
||||
// tax rate
|
||||
rate: function(doc, cdt, cdn) {
|
||||
this.calculate_taxes_and_totals();
|
||||
},
|
||||
|
||||
@ -372,7 +373,6 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
|
||||
var on_previous_row_error = function(row_range) {
|
||||
var msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included",
|
||||
[tax.idx, __(tax.doctype), tax.charge_type, row_range])
|
||||
|
||||
frappe.throw(msg);
|
||||
};
|
||||
|
||||
@ -680,14 +680,14 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
|
||||
});
|
||||
},
|
||||
|
||||
calculate_total_advance: function(parenttype, advance_parentfield) {
|
||||
calculate_total_advance: function(parenttype, advance_parentfield, update_paid_amount) {
|
||||
if(this.frm.doc.doctype == parenttype && this.frm.doc.docstatus < 2) {
|
||||
var advance_doclist = this.frm.doc[advance_parentfield] || [];
|
||||
this.frm.doc.total_advance = flt(frappe.utils.sum(
|
||||
$.map(advance_doclist, function(adv) { return adv.allocated_amount })
|
||||
), precision("total_advance"));
|
||||
|
||||
this.calculate_outstanding_amount();
|
||||
this.calculate_outstanding_amount(update_paid_amount);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -235,9 +235,9 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
}
|
||||
},
|
||||
|
||||
calculate_taxes_and_totals: function() {
|
||||
calculate_taxes_and_totals: function(update_paid_amount) {
|
||||
this._super();
|
||||
this.calculate_total_advance("Sales Invoice", "advance_adjustment_details");
|
||||
this.calculate_total_advance("Sales Invoice", "advance_adjustment_details", update_paid_amount);
|
||||
this.calculate_commission();
|
||||
this.calculate_contribution();
|
||||
|
||||
@ -398,7 +398,7 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
return grand_total_for_discount_amount;
|
||||
},
|
||||
|
||||
calculate_outstanding_amount: function() {
|
||||
calculate_outstanding_amount: function(update_paid_amount) {
|
||||
// NOTE:
|
||||
// paid_amount and write_off_amount is only for POS Invoice
|
||||
// total_advance is only for non POS Invoice
|
||||
@ -408,7 +408,9 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
var total_amount_to_pay = this.frm.doc.grand_total - this.frm.doc.write_off_amount
|
||||
- this.frm.doc.total_advance;
|
||||
if(this.frm.doc.is_pos) {
|
||||
if(!this.frm.doc.paid_amount) this.frm.doc.paid_amount = flt(total_amount_to_pay);
|
||||
if(!this.frm.doc.paid_amount || update_paid_amount===undefined || update_paid_amount) {
|
||||
this.frm.doc.paid_amount = flt(total_amount_to_pay);
|
||||
}
|
||||
} else {
|
||||
this.frm.doc.paid_amount = 0
|
||||
}
|
||||
|
@ -58,13 +58,14 @@ class LandedCostWizard(Document):
|
||||
ch.rate = amt
|
||||
ch.tax_amount = amt
|
||||
ch.docstatus = 1
|
||||
ch.save(1)
|
||||
ch.db_insert()
|
||||
else: # overwrite if exists
|
||||
matched_row[0].rate = amt
|
||||
matched_row[0].tax_amount = amt
|
||||
matched_row[0].cost_center = lc.cost_center
|
||||
|
||||
pr_doc.run_method("validate")
|
||||
pr_doc._validate_mandatory()
|
||||
for d in pr_doc.get_all_children():
|
||||
d.db_update()
|
||||
|
||||
|
@ -8,3 +8,9 @@ cur_frm.add_fetch("item_code", "item_name", "item_name")
|
||||
cur_frm.add_fetch("item_code", "description", "description")
|
||||
cur_frm.add_fetch("item_code", "item_group", "item_group")
|
||||
cur_frm.add_fetch("item_code", "brand", "brand")
|
||||
|
||||
cur_frm.cscript.onload = function() {
|
||||
cur_frm.set_query("item_code", function() {
|
||||
return erpnext.queries.item({"is_stock_item": "Yes", "has_serial_no": "Yes"})
|
||||
});
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ class NotUpdateStockError(frappe.ValidationError): pass
|
||||
class StockOverReturnError(frappe.ValidationError): pass
|
||||
class IncorrectValuationRateError(frappe.ValidationError): pass
|
||||
class DuplicateEntryForProductionOrderError(frappe.ValidationError): pass
|
||||
class StockOverProductionError(frappe.ValidationError): pass
|
||||
|
||||
from erpnext.controllers.stock_controller import StockController
|
||||
|
||||
@ -314,24 +313,11 @@ class StockEntry(StockController):
|
||||
if self.production_order:
|
||||
pro_doc = frappe.get_doc("Production Order", self.production_order)
|
||||
_validate_production_order(pro_doc)
|
||||
self.update_produced_qty(pro_doc)
|
||||
pro_doc.run_method("update_status")
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
pro_doc.run_method("update_produced_qty")
|
||||
self.update_planned_qty(pro_doc)
|
||||
|
||||
def update_produced_qty(self, pro_doc):
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
produced_qty = flt(pro_doc.produced_qty) + \
|
||||
(self.docstatus==1 and 1 or -1 ) * flt(self.fg_completed_qty)
|
||||
|
||||
if produced_qty > flt(pro_doc.qty):
|
||||
frappe.throw(_("Production Order") + ": " + self.production_order + "\n" +
|
||||
_("Total Manufactured Qty can not be greater than Planned qty to manufacture")
|
||||
+ "(%s/%s)" % (produced_qty, flt(pro_doc.qty)), StockOverProductionError)
|
||||
|
||||
status = 'Completed' if flt(produced_qty) >= flt(pro_doc.qty) else 'In Process'
|
||||
frappe.db.sql("""update `tabProduction Order` set status=%s, produced_qty=%s
|
||||
where name=%s""", (status, produced_qty, self.production_order))
|
||||
|
||||
def update_planned_qty(self, pro_doc):
|
||||
from erpnext.stock.utils import update_bin
|
||||
update_bin({
|
||||
|
@ -66,7 +66,6 @@ cur_frm.add_fetch('serial_no', 'warranty_expiry_date', 'warranty_expiry_date');
|
||||
cur_frm.add_fetch('serial_no', 'amc_expiry_date', 'amc_expiry_date');
|
||||
cur_frm.add_fetch('serial_no', 'customer', 'customer');
|
||||
cur_frm.add_fetch('serial_no', 'customer_name', 'customer_name');
|
||||
cur_frm.add_fetch('serial_no', 'delivery_address', 'customer_address');
|
||||
cur_frm.add_fetch('item_code', 'item_name', 'item_name');
|
||||
cur_frm.add_fetch('item_code', 'description', 'description');
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user