diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 83c6069e524..16629d9cf24 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '10.1.37' +__version__ = '10.1.38' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 909856ceaa4..bb7d55bc265 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -422,10 +422,10 @@ def get_timeline_data(doctype, name): # fetch and append data from Activity Log data += frappe.db.sql("""select {fields} from `tabActivity Log` - where reference_doctype='{doctype}' and reference_name='{name}' + where reference_doctype="{doctype}" and reference_name="{name}" and status!='Success' and creation > {after} {group_by} order by creation desc - """.format(doctype=doctype, name=name, fields=fields, + """.format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields, group_by=group_by, after=after), as_dict=False) timeline_items = dict(data) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index bf906cec07f..16768b93fcc 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -297,7 +297,10 @@ class ReceivablePayableReport(object): if party_type == "Supplier": for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date - from `tabPurchase Invoice` where docstatus=1""", as_dict=1): + from `tabPurchase Invoice` where docstatus = 1 + union + select name, due_date, bill_no, bill_date from `tabJournal Entry` + where docstatus = 1 and bill_no is not NULL""", as_dict=1): voucher_details.setdefault(pi.name, pi) return voucher_details diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 85c58deb3c3..6c0347f7a07 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -608,16 +608,19 @@ def get_itemised_tax(taxes): for item_code, tax_data in item_tax_map.items(): itemised_tax.setdefault(item_code, frappe._dict()) + tax_rate = 0.0 + tax_amount = 0.0 + if isinstance(tax_data, list): - itemised_tax[item_code][tax.description] = frappe._dict(dict( - tax_rate=flt(tax_data[0]), - tax_amount=flt(tax_data[1]) - )) + tax_rate = flt(tax_data[0]) + tax_amount = flt(tax_data[1]) else: - itemised_tax[item_code][tax.description] = frappe._dict(dict( - tax_rate=flt(tax_data), - tax_amount=0.0 - )) + tax_rate = flt(tax_data) + + itemised_tax[item_code][tax.description] = frappe._dict(dict( + tax_rate = tax_rate, + tax_amount = tax_amount + )) return itemised_tax diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index d6d8b1c73a9..25a42f2b094 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -56,6 +56,7 @@ class LeaveApplication(Document): self.status = "Cancelled" # notify leave applier about cancellation self.notify_employee() + self.cancel_attendance() def validate_applicable_after(self): if self.leave_type: @@ -149,6 +150,13 @@ class LeaveApplication(Document): doc.insert(ignore_permissions=True) doc.submit() + def cancel_attendance(self): + if self.docstatus == 2: + attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s\ + and (attendance_date between %s and %s) and docstatus < 2 and status in ('On Leave', 'Half Day')""",(self.employee, self.from_date, self.to_date), as_dict=1) + for name in attendance: + frappe.db.set_value("Attendance", name, "docstatus", 2) + def validate_salary_processed_days(self): if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"): return diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 09e49257413..03f1cd86298 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -18,8 +18,12 @@ from erpnext.hr.doctype.employee_benefit_application.employee_benefit_applicatio from erpnext.hr.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount class SalarySlip(TransactionBase): + def __init__(self, *args, **kwargs): + super(SalarySlip, self).__init__(*args, **kwargs) + self.series = 'Sal Slip/{0}/.#####'.format(self.employee) + def autoname(self): - self.name = make_autoname('Sal Slip/' +self.employee + '/.#####') + self.name = make_autoname(self.series) def validate(self): self.status = self.get_status() @@ -460,6 +464,10 @@ class SalarySlip(TransactionBase): self.set_status() self.update_status() + def on_trash(self): + from frappe.model.naming import revert_series_if_last + revert_series_if_last(self.series, self.name) + def email_salary_slip(self): receiver = frappe.db.get_value("Employee", self.employee, "prefered_email") diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b809eafefac..e933f5a7115 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -162,7 +162,7 @@ class BOM(WebsiteGenerator): if not self.buying_price_list: frappe.throw(_("Please select Price List")) rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list, - "item_code": arg["item_code"]}, "price_list_rate") + "item_code": arg["item_code"]}, "price_list_rate") or 0.0 price_list_currency = frappe.db.get_value("Price List", self.buying_price_list, "currency") diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 0047186053d..cd32e3e8571 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -340,12 +340,18 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) { // store tax breakup for each item - var key = item.item_code || item.item_name; - var item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate; - if (tax.item_wise_tax_detail && tax.item_wise_tax_detail[key]) - item_wise_tax_amount += tax.item_wise_tax_detail[key][1]; + let tax_detail = tax.item_wise_tax_detail; - tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))]; + let key = item.item_code; + if(item.item_name && !Object.keys(tax_detail).includes(item.item_name)) { + key = item.item_name; + } + + let item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate; + if (tax_detail && tax_detail[key]) + item_wise_tax_amount += tax_detail[key][1]; + + tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))]; }, round_off_totals: function(tax) { diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 690fbbcb3a2..27390d404c7 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -144,17 +144,10 @@ class Gstr1Report(object): """ % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1) for d in items: - item_details = {} - item_details[d.item_code] = d.base_net_amount - - if d.parent in self.invoice_items: - parent_dict = self.invoice_items[d.parent] - if d.item_code in parent_dict: - item_details[d.item_code] += parent_dict[d.item_code] - else: - item_details.update(parent_dict) - - self.invoice_items[d.parent] = item_details + if d.item_code not in self.invoice_items.get(d.parent, {}): + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, + sum(i.get('base_net_amount', 0) for i in items + if i.item_code == d.item_code and i.parent == d.parent)) def get_items_based_on_tax_rate(self): self.tax_details = frappe.db.sql(""" diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index eed401626ef..7d822a76766 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -310,7 +310,7 @@ class StockEntry(StockController): d.actual_qty = previous_sle.get("qty_after_transaction") or 0 # validate qty during submit - if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty: + if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and flt(d.actual_qty, d.precision("actual_qty")) < flt(d.transfer_qty, d.precision("actual_qty")): frappe.throw(_("Row {0}: Qty not available for {4} in warehouse {1} at posting time of the entry ({2} {3})").format(d.idx, frappe.bold(d.s_warehouse), formatdate(self.posting_date), format_time(self.posting_time), frappe.bold(d.item_code)) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index b5d2e3f790f..085e87f5f41 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -77,8 +77,8 @@ class StockLedgerEntry(Document): elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, self.item_code)) - elif item_det.has_batch_no ==0 and self.batch_no: - frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) + elif item_det.has_batch_no ==0 and self.batch_no and self.is_cancelled == "No": + frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) if item_det.has_variants: frappe.throw(_("Stock cannot exist for Item {0} since has variants").format(self.item_code),