Merge branch 'develop' of https://github.com/frappe/erpnext into develop
diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
index 5c3519a..02b0c4d 100644
--- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
+++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json
@@ -18,7 +18,8 @@
"in_list_view": 1,
"label": "Invoice",
"options": "Sales Invoice",
- "reqd": 1
+ "reqd": 1,
+ "search_index": 1
},
{
"fetch_from": "sales_invoice.customer",
@@ -60,7 +61,7 @@
}
],
"istable": 1,
- "modified": "2019-09-26 11:05:36.016772",
+ "modified": "2020-02-20 16:16:20.724620",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Discounted Invoice",
diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
index 93dfcc1..109737f 100644
--- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
+++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py
@@ -7,4 +7,4 @@
from frappe.model.document import Document
class DiscountedInvoice(Document):
- pass
+ pass
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 041e419..f9e4fd7 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -232,11 +232,36 @@
if bal < 0 and not on_cancel:
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
- # Update outstanding amt on against voucher
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
+ update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal)
+
+def update_outstanding_amt_in_ref(against_voucher, against_voucher_type, bal):
+ data = []
+ # Update outstanding amt on against voucher
+ if against_voucher_type == "Fees":
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
ref_doc.db_set('outstanding_amount', bal)
ref_doc.set_status(update=True)
+ return
+ elif against_voucher_type == "Purchase Invoice":
+ from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_status
+ data = frappe.db.get_value(against_voucher_type, against_voucher,
+ ["name as purchase_invoice", "outstanding_amount",
+ "is_return", "due_date", "docstatus"])
+ elif against_voucher_type == "Sales Invoice":
+ from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_status
+ data = frappe.db.get_value(against_voucher_type, against_voucher,
+ ["name as sales_invoice", "outstanding_amount", "is_discounted",
+ "is_return", "due_date", "docstatus"])
+
+ precision = frappe.get_precision(against_voucher_type, "outstanding_amount")
+ data = list(data)
+ data.append(precision)
+ status = get_status(data)
+ frappe.db.set_value(against_voucher_type, against_voucher, {
+ 'outstanding_amount': bal,
+ 'status': status
+ })
def validate_frozen_account(account, adv_adj=None):
frozen_account = frappe.db.get_value("Account", account, "freeze_account")
@@ -274,6 +299,9 @@
if d.against != new_against:
frappe.db.set_value("GL Entry", d.name, "against", new_against)
+def on_doctype_update():
+ frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
+ frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
def rename_gle_sle_docs():
for doctype in ["GL Entry", "Stock Ledger Entry"]:
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index a68c368..847fbed 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -125,6 +125,27 @@
else:
self.remarks = _("No Remarks")
+ def set_status(self, update=False, status=None, update_modified=True):
+ if self.is_new():
+ if self.get('amended_from'):
+ self.status = 'Draft'
+ return
+
+ if not status:
+ precision = self.precision("outstanding_amount")
+ args = [
+ self.name,
+ self.outstanding_amount,
+ self.is_return,
+ self.due_date,
+ self.docstatus,
+ precision
+ ]
+ status = get_status(args)
+
+ if update:
+ self.db_set('status', status, update_modified = update_modified)
+
def set_missing_values(self, for_validate=False):
if not self.credit_to:
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
@@ -1007,6 +1028,34 @@
# calculate totals again after applying TDS
self.calculate_taxes_and_totals()
+def get_status(*args):
+ purchase_invoice, outstanding_amount, is_return, due_date, docstatus, precision = args[0]
+
+ outstanding_amount = flt(outstanding_amount, precision)
+ due_date = getdate(due_date)
+ now_date = getdate()
+
+ if docstatus == 2:
+ status = "Cancelled"
+ elif docstatus == 1:
+ if outstanding_amount > 0 and due_date < now_date:
+ status = "Overdue"
+ elif outstanding_amount > 0 and due_date >= now_date:
+ status = "Unpaid"
+ #Check if outstanding amount is 0 due to debit note issued against invoice
+ elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': purchase_invoice, 'docstatus': 1}):
+ status = "Debit Note Issued"
+ elif is_return == 1:
+ status = "Return"
+ elif outstanding_amount <=0:
+ status = "Paid"
+ else:
+ status = "Submitted"
+ else:
+ status = "Draft"
+
+ return status
+
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 658e703..bcaa394 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1217,62 +1217,83 @@
self.set_missing_values(for_validate = True)
- def get_discounting_status(self):
- status = None
- if self.is_discounted:
- invoice_discounting_list = frappe.db.sql("""
- select status
- from `tabInvoice Discounting` id, `tabDiscounted Invoice` d
- where
- id.name = d.parent
- and d.sales_invoice=%s
- and id.docstatus=1
- and status in ('Disbursed', 'Settled')
- """, self.name)
- for d in invoice_discounting_list:
- status = d[0]
- if status == "Disbursed":
- break
- return status
-
def set_status(self, update=False, status=None, update_modified=True):
if self.is_new():
if self.get('amended_from'):
self.status = 'Draft'
return
- precision = self.precision("outstanding_amount")
- outstanding_amount = flt(self.outstanding_amount, precision)
- due_date = getdate(self.due_date)
- nowdate = getdate()
- discountng_status = self.get_discounting_status()
-
if not status:
- if self.docstatus == 2:
- status = "Cancelled"
- elif self.docstatus == 1:
- if outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
- self.status = "Overdue and Discounted"
- elif outstanding_amount > 0 and due_date < nowdate:
- self.status = "Overdue"
- elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed':
- self.status = "Unpaid and Discounted"
- elif outstanding_amount > 0 and due_date >= nowdate:
- self.status = "Unpaid"
- #Check if outstanding amount is 0 due to credit note issued against invoice
- elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
- self.status = "Credit Note Issued"
- elif self.is_return == 1:
- self.status = "Return"
- elif outstanding_amount<=0:
- self.status = "Paid"
- else:
- self.status = "Submitted"
- else:
- self.status = "Draft"
+ precision = self.precision("outstanding_amount")
+ args = [
+ self.name,
+ self.outstanding_amount,
+ self.is_discounted,
+ self.is_return,
+ self.due_date,
+ self.docstatus,
+ precision,
+ ]
+ status = get_status(args)
if update:
- self.db_set('status', self.status, update_modified = update_modified)
+ self.db_set('status', status, update_modified = update_modified)
+
+def get_discounting_status(sales_invoice):
+ status = None
+
+ invoice_discounting_list = frappe.db.sql("""
+ select status
+ from `tabInvoice Discounting` id, `tabDiscounted Invoice` d
+ where
+ id.name = d.parent
+ and d.sales_invoice=%s
+ and id.docstatus=1
+ and status in ('Disbursed', 'Settled')
+ """, sales_invoice)
+
+ for d in invoice_discounting_list:
+ status = d[0]
+ if status == "Disbursed":
+ break
+
+ return status
+
+def get_status(*args):
+ sales_invoice, outstanding_amount, is_discounted, is_return, due_date, docstatus, precision = args[0]
+
+ discounting_status = None
+ if is_discounted:
+ discounting_status = get_discounting_status(sales_invoice)
+
+ outstanding_amount = flt(outstanding_amount, precision)
+ due_date = getdate(due_date)
+ now_date = getdate()
+
+ if docstatus == 2:
+ status = "Cancelled"
+ elif docstatus == 1:
+ if outstanding_amount > 0 and due_date < now_date and is_discounted and discounting_status=='Disbursed':
+ status = "Overdue and Discounted"
+ elif outstanding_amount > 0 and due_date < now_date:
+ status = "Overdue"
+ elif outstanding_amount > 0 and due_date >= now_date and is_discounted and discounting_status=='Disbursed':
+ status = "Unpaid and Discounted"
+ elif outstanding_amount > 0 and due_date >= now_date:
+ status = "Unpaid"
+ #Check if outstanding amount is 0 due to credit note issued against invoice
+ elif outstanding_amount <= 0 and is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': sales_invoice, 'docstatus': 1}):
+ status = "Credit Note Issued"
+ elif is_return == 1:
+ status = "Return"
+ elif outstanding_amount <=0:
+ status = "Paid"
+ else:
+ status = "Submitted"
+ else:
+ status = "Draft"
+
+ return status
def validate_inter_company_party(doctype, party, company, inter_company_reference):
if not party:
@@ -1444,7 +1465,7 @@
"party": party,
"company": company
}
-
+
def get_internal_party(parties, link_doctype, doc):
if len(parties) == 1:
party = parties[0].name
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index bb1b7e3..6d53530 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -140,8 +140,11 @@
gle = frappe.get_doc(args)
gle.flags.ignore_permissions = 1
gle.flags.from_repost = from_repost
- gle.insert()
+ gle.validate()
+ gle.flags.ignore_permissions = True
+ gle.db_insert()
gle.run_method("on_update_with_args", adv_adj, update_outstanding, from_repost)
+ gle.flags.ignore_validate = True
gle.submit()
def validate_account_for_perpetual_inventory(gl_map):
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 8b275a6..b465a10 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -44,17 +44,6 @@
["Closed", "eval:self.status=='Closed'"],
["On Hold", "eval:self.status=='On Hold'"],
],
- "Purchase Invoice": [
- ["Draft", None],
- ["Submitted", "eval:self.docstatus==1"],
- ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
- ["Return", "eval:self.is_return==1 and self.docstatus==1"],
- ["Debit Note Issued",
- "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
- ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
- ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
- ["Cancelled", "eval:self.docstatus==2"],
- ],
"Purchase Order": [
["Draft", None],
["To Receive and Bill", "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1"],
diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.py b/erpnext/hr/doctype/additional_salary/additional_salary.py
index 8498b3d..bc7dcee 100644
--- a/erpnext/hr/doctype/additional_salary/additional_salary.py
+++ b/erpnext/hr/doctype/additional_salary/additional_salary.py
@@ -39,19 +39,21 @@
return amount_per_day * no_of_days
@frappe.whitelist()
-def get_additional_salary_component(employee, start_date, end_date):
+def get_additional_salary_component(employee, start_date, end_date, component_type):
additional_components = frappe.db.sql("""
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
from `tabAdditional Salary`
where employee=%(employee)s
and docstatus = 1
and payroll_date between %(from_date)s and %(to_date)s
+ and type = %(component_type)s
group by salary_component, overwrite_salary_structure_amount
order by salary_component, overwrite_salary_structure_amount
""", {
'employee': employee,
'from_date': start_date,
- 'to_date': end_date
+ 'to_date': end_date,
+ 'component_type': "Earning" if component_type == "earnings" else "Deduction"
}, as_dict=1)
additional_components_list = []
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index eee7974..d03a3dd 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -299,9 +299,11 @@
def calculate_net_pay(self):
if self.salary_structure:
- self.calculate_component_amounts()
-
+ self.calculate_component_amounts("earnings")
self.gross_pay = self.get_component_totals("earnings")
+
+ if self.salary_structure:
+ self.calculate_component_amounts("deductions")
self.total_deduction = self.get_component_totals("deductions")
self.set_loan_repayment()
@@ -309,25 +311,27 @@
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
self.rounded_total = rounded(self.net_pay)
- def calculate_component_amounts(self):
+ def calculate_component_amounts(self, component_type):
if not getattr(self, '_salary_structure_doc', None):
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
payroll_period = get_payroll_period(self.start_date, self.end_date, self.company)
- self.add_structure_components()
- self.add_employee_benefits(payroll_period)
- self.add_additional_salary_components()
- self.add_tax_components(payroll_period)
- self.set_component_amounts_based_on_payment_days()
+ self.add_structure_components(component_type)
+ self.add_additional_salary_components(component_type)
+ if component_type == "earnings":
+ self.add_employee_benefits(payroll_period)
+ else:
+ self.add_tax_components(payroll_period)
- def add_structure_components(self):
+ self.set_component_amounts_based_on_payment_days(component_type)
+
+ def add_structure_components(self, component_type):
data = self.get_data_for_eval()
- for key in ('earnings', 'deductions'):
- for struct_row in self._salary_structure_doc.get(key):
- amount = self.eval_condition_and_formula(struct_row, data)
- if amount and struct_row.statistical_component == 0:
- self.update_component_row(struct_row, amount, key)
+ for struct_row in self._salary_structure_doc.get(component_type):
+ amount = self.eval_condition_and_formula(struct_row, data)
+ if amount and struct_row.statistical_component == 0:
+ self.update_component_row(struct_row, amount, component_type)
def get_data_for_eval(self):
'''Returns data for evaluating formula'''
@@ -400,14 +404,15 @@
amount = last_benefit.amount
self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
- def add_additional_salary_components(self):
- additional_components = get_additional_salary_component(self.employee, self.start_date, self.end_date)
+ def add_additional_salary_components(self, component_type):
+ additional_components = get_additional_salary_component(self.employee,
+ self.start_date, self.end_date, component_type)
if additional_components:
for additional_component in additional_components:
amount = additional_component.amount
overwrite = additional_component.overwrite
- key = "earnings" if additional_component.type == "Earning" else "deductions"
- self.update_component_row(frappe._dict(additional_component.struct_row), amount, key, overwrite=overwrite)
+ self.update_component_row(frappe._dict(additional_component.struct_row), amount,
+ component_type, overwrite=overwrite)
def add_tax_components(self, payroll_period):
# Calculate variable_based_on_taxable_salary after all components updated in salary slip
@@ -736,7 +741,7 @@
total += d.amount
return total
- def set_component_amounts_based_on_payment_days(self):
+ def set_component_amounts_based_on_payment_days(self, component_type):
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
@@ -746,9 +751,8 @@
if not joining_date:
frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
- for component_type in ("earnings", "deductions"):
- for d in self.get(component_type):
- d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
+ for d in self.get(component_type):
+ d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
def set_loan_repayment(self):
self.set('loans', [])
diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
index 7815094..6ca6dfd 100644
--- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
@@ -25,7 +25,6 @@
make_employee("test_employee@salary.com")
make_employee("test_employee_2@salary.com")
-
def make_holiday_list(self):
if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
holiday_list = frappe.get_doc({
@@ -38,6 +37,29 @@
holiday_list.get_weekly_off_dates()
holiday_list.save()
+ def test_salary_structure_deduction_based_on_gross_pay(self):
+
+ emp = make_employee("test_employee_3@salary.com")
+
+ sal_struct = make_salary_structure("Salary Structure 2", "Monthly", dont_submit = True)
+
+ sal_struct.earnings = [sal_struct.earnings[0]]
+ sal_struct.earnings[0].amount_based_on_formula = 1
+ sal_struct.earnings[0].formula = "base"
+
+ sal_struct.deductions = [sal_struct.deductions[0]]
+
+ sal_struct.deductions[0].amount_based_on_formula = 1
+ sal_struct.deductions[0].condition = "gross_pay > 100"
+ sal_struct.deductions[0].formula = "gross_pay * 0.2"
+
+ sal_struct.submit()
+
+ assignment = create_salary_structure_assignment(emp, "Salary Structure 2")
+ ss = make_salary_slip(sal_struct.name, employee = emp)
+
+ self.assertEqual(assignment.base * 0.2, ss.deductions[0].amount)
+
def test_amount_totals(self):
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 1503adb..8ecc13b 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -108,8 +108,8 @@
def delete_communications(doctype, company_name, company_fieldname):
reference_docs = frappe.get_all(doctype, filters={company_fieldname:company_name})
reference_doc_names = [r.name for r in reference_docs]
-
+
communications = frappe.get_all("Communication", filters={"reference_doctype":doctype,"reference_name":["in", reference_doc_names]})
communication_names = [c.name for c in communications]
- frappe.delete_doc("Communication", communication_names)
+ frappe.delete_doc("Communication", communication_names, ignore_permissions=True)
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index d34f420..64d4c6c 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -205,6 +205,7 @@
def validate_serial_no(sle, item_det):
serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else []
+ validate_material_transfer_entry(sle)
if item_det.has_serial_no==0:
if serial_nos:
@@ -224,7 +225,9 @@
for serial_no in serial_nos:
if frappe.db.exists("Serial No", serial_no):
- sr = frappe.get_doc("Serial No", serial_no)
+ sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order",
+ "delivery_document_no", "delivery_document_type", "warehouse",
+ "purchase_document_no", "company"], as_dict=1)
if sr.item_code!=sle.item_code:
if not allow_serial_nos_with_different_item(serial_no, sle):
@@ -305,6 +308,19 @@
frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
.format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse))
+def validate_material_transfer_entry(sle_doc):
+ sle_doc.update({
+ "skip_update_serial_no": False,
+ "skip_serial_no_validaiton": False
+ })
+
+ if (sle_doc.voucher_type == "Stock Entry" and sle_doc.is_cancelled == "No" and
+ frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"):
+ if sle_doc.actual_qty < 0:
+ sle_doc.skip_update_serial_no = True
+ else:
+ sle_doc.skip_serial_no_validaiton = True
+
def validate_so_serial_no(sr, sales_order,):
if not sr.sales_order or sr.sales_order!= sales_order:
frappe.throw(_("""Sales Order {0} has reservation for item {1}, you can
@@ -312,7 +328,8 @@
be delivered""").format(sales_order, sr.item_code, sr.name))
def has_duplicate_serial_no(sn, sle):
- if sn.warehouse and sle.voucher_type != 'Stock Reconciliation':
+ if (sn.warehouse and not sle.skip_serial_no_validaiton
+ and sle.voucher_type != 'Stock Reconciliation'):
return True
if sn.company != sle.company:
@@ -337,7 +354,7 @@
"""
allow_serial_nos = False
if sle.voucher_type=="Stock Entry" and cint(sle.actual_qty) > 0:
- stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no)
+ stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no)
if stock_entry.purpose in ("Repack", "Manufacture"):
for d in stock_entry.get("items"):
if d.serial_no and (d.s_warehouse if sle.is_cancelled=="No" else d.t_warehouse):
@@ -348,6 +365,7 @@
return allow_serial_nos
def update_serial_nos(sle, item_det):
+ if sle.skip_update_serial_no: return
if sle.is_cancelled == "No" and not sle.serial_no and cint(sle.actual_qty) > 0 \
and item_det.has_serial_no == 1 and item_det.serial_no_series:
serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty)
@@ -369,22 +387,16 @@
voucher_type = args.get('voucher_type')
item_code = args.get('item_code')
for serial_no in serial_nos:
+ is_new = False
if frappe.db.exists("Serial No", serial_no):
- sr = frappe.get_doc("Serial No", serial_no)
- sr.via_stock_ledger = True
- sr.item_code = item_code
- sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None
- sr.batch_no = args.get('batch_no')
- sr.location = args.get('location')
- sr.company = args.get('company')
- sr.supplier = args.get('supplier')
- if sr.sales_order and voucher_type == "Stock Entry" \
- and not args.get('actual_qty', 0) > 0:
- sr.sales_order = None
- sr.update_serial_no_reference()
- sr.save(ignore_permissions=True)
+ sr = frappe.get_cached_doc("Serial No", serial_no)
elif args.get('actual_qty', 0) > 0:
- created_numbers.append(make_serial_no(serial_no, args))
+ sr = frappe.new_doc("Serial No")
+ is_new = True
+
+ sr = update_args_for_serial_no(sr, serial_no, args, is_new=is_new)
+ if is_new:
+ created_numbers.append(sr.name)
form_links = list(map(lambda d: frappe.utils.get_link_to_form('Serial No', d), created_numbers))
@@ -419,20 +431,34 @@
return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n')
if s.strip()]
-def make_serial_no(serial_no, args):
- sr = frappe.new_doc("Serial No")
- sr.serial_no = serial_no
- sr.item_code = args.get('item_code')
- sr.company = args.get('company')
- sr.batch_no = args.get('batch_no')
- sr.via_stock_ledger = args.get('via_stock_ledger') or True
- sr.warehouse = args.get('warehouse')
+def update_args_for_serial_no(serial_no_doc, serial_no, args, is_new=False):
+ serial_no_doc.update({
+ "item_code": args.get("item_code"),
+ "company": args.get("company"),
+ "batch_no": args.get("batch_no"),
+ "via_stock_ledger": args.get("via_stock_ledger") or True,
+ "supplier": args.get("supplier"),
+ "location": args.get("location"),
+ "warehouse": (args.get("warehouse")
+ if args.get("actual_qty", 0) > 0 else None)
+ })
- sr.validate_item()
- sr.update_serial_no_reference(serial_no)
- sr.db_insert()
+ if is_new:
+ serial_no_doc.serial_no = serial_no
- return sr.name
+ if (serial_no_doc.sales_order and args.get("voucher_type") == "Stock Entry"
+ and not args.get("actual_qty", 0) > 0):
+ serial_no_doc.sales_order = None
+
+ serial_no_doc.validate_item()
+ serial_no_doc.update_serial_no_reference(serial_no)
+
+ if is_new:
+ serial_no_doc.db_insert()
+ else:
+ serial_no_doc.db_update()
+
+ return serial_no_doc
def update_serial_nos_after_submit(controller, parentfield):
stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no, actual_qty, warehouse
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index c9eba71..c03eb79 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -240,6 +240,7 @@
"options": "Company",
"print_width": "150px",
"read_only": 1,
+ "search_index": 1,
"width": "150px"
},
{
@@ -274,7 +275,7 @@
"icon": "fa fa-list",
"idx": 1,
"in_create": 1,
- "modified": "2019-11-27 12:17:31.522675",
+ "modified": "2020-02-25 22:53:33.504681",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 2c6c953..f3381c7 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -136,7 +136,7 @@
bin_obj.flags.ignore_permissions = 1
bin_obj.insert()
else:
- bin_obj = frappe.get_doc('Bin', bin)
+ bin_obj = frappe.get_cached_doc('Bin', bin)
bin_obj.flags.ignore_permissions = True
return bin_obj