Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into gst_sales_register_fix

This commit is contained in:
Deepesh Garg 2021-07-26 17:21:13 +05:30
commit 0011585415
45 changed files with 178 additions and 95 deletions

View File

@ -8,18 +8,3 @@ rules:
dynamic content. Avoid it or use safe_eval(). dynamic content. Avoid it or use safe_eval().
languages: [python] languages: [python]
severity: ERROR severity: ERROR
- id: frappe-sqli-format-strings
patterns:
- pattern-inside: |
@frappe.whitelist()
def $FUNC(...):
...
- pattern-either:
- pattern: frappe.db.sql("..." % ...)
- pattern: frappe.db.sql(f"...", ...)
- pattern: frappe.db.sql("...".format(...), ...)
message: |
Detected use of raw string formatting for SQL queries. This can lead to sql injection vulnerabilities. Refer security guidelines - https://github.com/frappe/erpnext/wiki/Code-Security-Guidelines
languages: [python]
severity: WARNING

View File

@ -99,7 +99,6 @@ class ReceivablePayableReport(object):
voucher_no = gle.voucher_no, voucher_no = gle.voucher_no,
party = gle.party, party = gle.party,
posting_date = gle.posting_date, posting_date = gle.posting_date,
remarks = gle.remarks,
account_currency = gle.account_currency, account_currency = gle.account_currency,
invoiced = 0.0, invoiced = 0.0,
paid = 0.0, paid = 0.0,
@ -579,7 +578,7 @@ class ReceivablePayableReport(object):
self.gl_entries = frappe.db.sql(""" self.gl_entries = frappe.db.sql("""
select select
name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center, name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center,
against_voucher_type, against_voucher, account_currency, remarks, {0} against_voucher_type, against_voucher, account_currency, {0}
from from
`tabGL Entry` `tabGL Entry`
where where
@ -792,8 +791,6 @@ class ReceivablePayableReport(object):
self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link', self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
options='Supplier Group') options='Supplier Group')
self.add_column(label=_('Remarks'), fieldname='remarks', fieldtype='Text', width=200)
def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120): def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120):
if not fieldname: fieldname = scrub(label) if not fieldname: fieldname = scrub(label)
if fieldtype=='Currency': options='currency' if fieldtype=='Currency': options='currency'

View File

@ -241,6 +241,7 @@ class GrossProfitGenerator(object):
sle.voucher_detail_no == row.item_row: sle.voucher_detail_no == row.item_row:
previous_stock_value = len(my_sle) > i+1 and \ previous_stock_value = len(my_sle) > i+1 and \
flt(my_sle[i+1].stock_value) or 0.0 flt(my_sle[i+1].stock_value) or 0.0
if previous_stock_value: if previous_stock_value:
return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty)) return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
else: else:
@ -335,7 +336,7 @@ class GrossProfitGenerator(object):
res = frappe.db.sql("""select item_code, voucher_type, voucher_no, res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
voucher_detail_no, stock_value, warehouse, actual_qty as qty voucher_detail_no, stock_value, warehouse, actual_qty as qty
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where company=%(company)s where company=%(company)s and is_cancelled = 0
order by order by
item_code desc, warehouse desc, posting_date desc, item_code desc, warehouse desc, posting_date desc,
posting_time desc, creation desc""", self.filters, as_dict=True) posting_time desc, creation desc""", self.filters, as_dict=True)

View File

@ -1112,8 +1112,11 @@ class AccountsController(TransactionBase):
for d in self.get("payment_schedule"): for d in self.get("payment_schedule"):
if d.invoice_portion: if d.invoice_portion:
d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount')) d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount')) d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount'))
d.outstanding = d.payment_amount d.outstanding = d.payment_amount
elif not d.invoice_portion:
d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount'))
def set_due_date(self): def set_due_date(self):
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date] due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]

View File

@ -407,6 +407,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
where where
batch.disabled = 0 batch.disabled = 0
and sle.is_cancelled = 0
and sle.item_code = %(item_code)s and sle.item_code = %(item_code)s
and sle.warehouse = %(warehouse)s and sle.warehouse = %(warehouse)s
and (sle.batch_no like %(txt)s and (sle.batch_no like %(txt)s

View File

@ -53,12 +53,17 @@ class StockController(AccountsController):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
for d in self.get("items"): for d in self.get("items"):
if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no: if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no:
serial_nos = get_serial_nos(d.serial_no) serial_nos = frappe.get_all("Serial No",
for serial_no_data in frappe.get_all("Serial No", fields=["batch_no", "name", "warehouse"],
filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]): filters={
if serial_no_data.batch_no != d.batch_no: "name": ("in", get_serial_nos(d.serial_no))
}
)
for row in serial_nos:
if row.warehouse and row.batch_no != d.batch_no:
frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}") frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}")
.format(d.idx, serial_no_data.name, d.batch_no)) .format(d.idx, row.name, d.batch_no))
if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2: if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date") expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")

View File

@ -24,7 +24,8 @@ doctype_js = {
"Address": "public/js/address.js", "Address": "public/js/address.js",
"Communication": "public/js/communication.js", "Communication": "public/js/communication.js",
"Event": "public/js/event.js", "Event": "public/js/event.js",
"Newsletter": "public/js/newsletter.js" "Newsletter": "public/js/newsletter.js",
"Contact": "public/js/contact.js"
} }
override_doctype_class = { override_doctype_class = {

View File

@ -9,7 +9,7 @@ from frappe.utils import flt, getdate
from frappe import _ from frappe import _
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name from erpnext.hr.utils import set_employee_name, validate_active_employee
class Appraisal(Document): class Appraisal(Document):
def validate(self): def validate(self):
@ -19,6 +19,7 @@ class Appraisal(Document):
if not self.goals: if not self.goals:
frappe.throw(_("Goals cannot be empty")) frappe.throw(_("Goals cannot be empty"))
validate_active_employee(self.employee)
set_employee_name(self) set_employee_name(self)
self.validate_dates() self.validate_dates()
self.validate_existing_appraisal() self.validate_existing_appraisal()

View File

@ -8,11 +8,13 @@ from frappe.utils import getdate, nowdate
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import cstr, get_datetime, formatdate from frappe.utils import cstr, get_datetime, formatdate
from erpnext.hr.utils import validate_active_employee
class Attendance(Document): class Attendance(Document):
def validate(self): def validate(self):
from erpnext.controllers.status_updater import validate_status from erpnext.controllers.status_updater import validate_status
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"]) validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
validate_active_employee(self.employee)
self.validate_attendance_date() self.validate_attendance_date()
self.validate_duplicate_record() self.validate_duplicate_record()
self.validate_employee_status() self.validate_employee_status()

View File

@ -8,10 +8,11 @@ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import date_diff, add_days, getdate from frappe.utils import date_diff, add_days, getdate
from erpnext.hr.doctype.employee.employee import is_holiday from erpnext.hr.doctype.employee.employee import is_holiday
from erpnext.hr.utils import validate_dates from erpnext.hr.utils import validate_dates, validate_active_employee
class AttendanceRequest(Document): class AttendanceRequest(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
validate_dates(self, self.from_date, self.to_date) validate_dates(self, self.from_date, self.to_date)
if self.half_day: if self.half_day:
if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date): if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date):

View File

@ -7,12 +7,13 @@ import frappe
from frappe import _ from frappe import _
from frappe.utils import date_diff, add_days, getdate, cint, format_date from frappe.utils import date_diff, add_days, getdate, cint, format_date
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \ from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, validate_active_employee, \
get_holidays_for_employee, create_additional_leave_ledger_entry get_holidays_for_employee, create_additional_leave_ledger_entry
class CompensatoryLeaveRequest(Document): class CompensatoryLeaveRequest(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
validate_dates(self, self.work_from_date, self.work_end_date) validate_dates(self, self.work_from_date, self.work_end_date)
if self.half_day: if self.half_day:
if not self.half_day_date: if not self.half_day_date:

View File

@ -13,8 +13,10 @@ from frappe.model.document import Document
from erpnext.utilities.transaction_base import delete_events from erpnext.utilities.transaction_base import delete_events
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet
class EmployeeUserDisabledError(frappe.ValidationError): pass class EmployeeUserDisabledError(frappe.ValidationError):
class EmployeeLeftValidationError(frappe.ValidationError): pass pass
class InactiveEmployeeStatusError(frappe.ValidationError):
pass
class Employee(NestedSet): class Employee(NestedSet):
nsm_parent_field = 'reports_to' nsm_parent_field = 'reports_to'
@ -196,7 +198,7 @@ class Employee(NestedSet):
message += "<br><br><ul><li>" + "</li><li>".join(link_to_employees) message += "<br><br><ul><li>" + "</li><li>".join(link_to_employees)
message += "</li></ul><br>" message += "</li></ul><br>"
message += _("Please make sure the employees above report to another Active employee.") message += _("Please make sure the employees above report to another Active employee.")
throw(message, EmployeeLeftValidationError, _("Cannot Relieve Employee")) throw(message, InactiveEmployeeStatusError, _("Cannot Relieve Employee"))
if not self.relieving_date: if not self.relieving_date:
throw(_("Please enter relieving date.")) throw(_("Please enter relieving date."))

View File

@ -7,7 +7,7 @@ import frappe
import erpnext import erpnext
import unittest import unittest
import frappe.utils import frappe.utils
from erpnext.hr.doctype.employee.employee import EmployeeLeftValidationError from erpnext.hr.doctype.employee.employee import InactiveEmployeeStatusError
test_records = frappe.get_test_records('Employee') test_records = frappe.get_test_records('Employee')
@ -45,10 +45,33 @@ class TestEmployee(unittest.TestCase):
employee2_doc.save() employee2_doc.save()
employee1_doc.reload() employee1_doc.reload()
employee1_doc.status = 'Left' employee1_doc.status = 'Left'
self.assertRaises(EmployeeLeftValidationError, employee1_doc.save) self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save)
def test_employee_status_inactive(self):
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list
employee = make_employee("test_employee_status@company.com")
employee_doc = frappe.get_doc("Employee", employee)
employee_doc.status = "Inactive"
employee_doc.save()
employee_doc.reload()
make_holiday_list()
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
frappe.db.sql("""delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'""")
salary_structure = make_salary_structure("Test Inactive Employee Salary Slip", "Monthly",
employee=employee_doc.name, company=employee_doc.company)
salary_slip = make_salary_slip(salary_structure.name, employee=employee_doc.name)
self.assertRaises(InactiveEmployeeStatusError, salary_slip.save)
def tearDown(self):
frappe.db.rollback()
def make_employee(user, company=None, **kwargs): def make_employee(user, company=None, **kwargs):
""
if not frappe.db.get_value("User", user): if not frappe.db.get_value("User", user):
frappe.get_doc({ frappe.get_doc({
"doctype": "User", "doctype": "User",
@ -80,4 +103,5 @@ def make_employee(user, company=None, **kwargs):
employee.insert() employee.insert()
return employee.name return employee.name
else: else:
frappe.db.set_value("Employee", {"employee_name":user}, "status", "Active")
return frappe.get_value("Employee", {"employee_name":user}, "name") return frappe.get_value("Employee", {"employee_name":user}, "name")

View File

@ -8,6 +8,7 @@ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import flt, nowdate from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
from erpnext.hr.utils import validate_active_employee
class EmployeeAdvanceOverPayment(frappe.ValidationError): class EmployeeAdvanceOverPayment(frappe.ValidationError):
pass pass
@ -18,6 +19,7 @@ class EmployeeAdvance(Document):
'make_payment_via_journal_entry') 'make_payment_via_journal_entry')
def validate(self): def validate(self):
validate_active_employee(self.employee)
self.set_status() self.set_status()
def on_cancel(self): def on_cancel(self):
@ -183,9 +185,9 @@ def make_return_entry(employee, company, employee_advance_name, return_amount,
bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment)
if not bank_cash_account: if not bank_cash_account:
frappe.throw(_("Please set a Default Cash Account in Company defaults")) frappe.throw(_("Please set a Default Cash Account in Company defaults"))
advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency') advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency')
je = frappe.new_doc('Journal Entry') je = frappe.new_doc('Journal Entry')
je.posting_date = nowdate() je.posting_date = nowdate()
je.voucher_type = get_voucher_type(mode_of_payment) je.voucher_type = get_voucher_type(mode_of_payment)

View File

@ -9,9 +9,11 @@ from frappe.model.document import Document
from frappe import _ from frappe import _
from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift
from erpnext.hr.utils import validate_active_employee
class EmployeeCheckin(Document): class EmployeeCheckin(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
self.validate_duplicate_log() self.validate_duplicate_log()
self.fetch_shift() self.fetch_shift()
@ -122,7 +124,7 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki
def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type): def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type):
"""Given a set of logs in chronological order calculates the total working hours based on the parameters. """Given a set of logs in chronological order calculates the total working hours based on the parameters.
Zero is returned for all invalid cases. Zero is returned for all invalid cases.
:param logs: The List of 'Employee Checkin'. :param logs: The List of 'Employee Checkin'.
:param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin' :param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin'
:param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out' :param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out'

View File

@ -7,12 +7,11 @@ import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import getdate from frappe.utils import getdate
from erpnext.hr.utils import update_employee from erpnext.hr.utils import update_employee, validate_active_employee
class EmployeePromotion(Document): class EmployeePromotion(Document):
def validate(self): def validate(self):
if frappe.get_value("Employee", self.employee, "status") != "Active": validate_active_employee(self.employee)
frappe.throw(_("Cannot promote Employee with status Left or Inactive"))
def before_submit(self): def before_submit(self):
if getdate(self.promotion_date) > getdate(): if getdate(self.promotion_date) > getdate():

View File

@ -7,9 +7,11 @@ import frappe
from frappe import _ from frappe import _
from frappe.utils import get_link_to_form from frappe.utils import get_link_to_form
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import validate_active_employee
class EmployeeReferral(Document): class EmployeeReferral(Document):
def validate(self): def validate(self):
validate_active_employee(self.referrer)
self.set_full_name() self.set_full_name()
self.set_referral_bonus_payment_status() self.set_referral_bonus_payment_status()

View File

@ -10,10 +10,6 @@ from frappe.utils import getdate
from erpnext.hr.utils import update_employee from erpnext.hr.utils import update_employee
class EmployeeTransfer(Document): class EmployeeTransfer(Document):
def validate(self):
if frappe.get_value("Employee", self.employee, "status") != "Active":
frappe.throw(_("Cannot transfer Employee with status Left or Inactive"))
def before_submit(self): def before_submit(self):
if getdate(self.transfer_date) > getdate(): if getdate(self.transfer_date) > getdate():
frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"), frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"),

View File

@ -6,7 +6,7 @@ import frappe, erpnext
from frappe import _ from frappe import _
from frappe.utils import get_fullname, flt, cstr, get_link_to_form from frappe.utils import get_fullname, flt, cstr, get_link_to_form
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name, share_doc_with_approver from erpnext.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee
from erpnext.accounts.party import get_party_account from erpnext.accounts.party import get_party_account
from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
@ -23,6 +23,7 @@ class ExpenseClaim(AccountsController):
'make_payment_via_journal_entry') 'make_payment_via_journal_entry')
def validate(self): def validate(self):
validate_active_employee(self.employee)
self.validate_advances() self.validate_advances()
self.validate_sanctioned_amount() self.validate_sanctioned_amount()
self.calculate_total_amount() self.calculate_total_amount()

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, get_fullname, add_days, nowdate from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, get_fullname, add_days, nowdate
from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver, validate_active_employee
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange
@ -22,6 +22,7 @@ class LeaveApplication(Document):
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type) return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
def validate(self): def validate(self):
validate_active_employee(self.employee)
set_employee_name(self) set_employee_name(self)
self.validate_dates() self.validate_dates()
self.validate_balance_leaves() self.validate_balance_leaves()

View File

@ -7,7 +7,7 @@ import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import getdate, nowdate, flt from frappe.utils import getdate, nowdate, flt
from erpnext.hr.utils import set_employee_name from erpnext.hr.utils import set_employee_name, validate_active_employee
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves
@ -15,6 +15,7 @@ from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leav
class LeaveEncashment(Document): class LeaveEncashment(Document):
def validate(self): def validate(self):
set_employee_name(self) set_employee_name(self)
validate_active_employee(self.employee)
self.get_leave_details_for_encashment() self.get_leave_details_for_encashment()
self.validate_salary_structure() self.validate_salary_structure()

View File

@ -9,10 +9,12 @@ from frappe.model.document import Document
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, now_datetime, nowdate from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, now_datetime, nowdate
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday
from erpnext.hr.utils import validate_active_employee
from datetime import timedelta, datetime from datetime import timedelta, datetime
class ShiftAssignment(Document): class ShiftAssignment(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
self.validate_overlapping_dates() self.validate_overlapping_dates()
if self.end_date and self.end_date <= self.start_date: if self.end_date and self.end_date <= self.start_date:

View File

@ -7,12 +7,13 @@ import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import formatdate, getdate from frappe.utils import formatdate, getdate
from erpnext.hr.utils import share_doc_with_approver from erpnext.hr.utils import share_doc_with_approver, validate_active_employee
class OverlapError(frappe.ValidationError): pass class OverlapError(frappe.ValidationError): pass
class ShiftRequest(Document): class ShiftRequest(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
self.validate_dates() self.validate_dates()
self.validate_shift_request_overlap_dates() self.validate_shift_request_overlap_dates()
self.validate_approver() self.validate_approver()

View File

@ -5,6 +5,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import validate_active_employee
class TravelRequest(Document): class TravelRequest(Document):
pass def validate(self):
validate_active_employee(self.employee)

View File

@ -3,13 +3,12 @@
import erpnext import erpnext
import frappe import frappe
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee, InactiveEmployeeStatusError
from frappe import _ from frappe import _
from frappe.desk.form import assign_to from frappe.desk.form import assign_to
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate, from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate,
get_datetime, getdate, nowdate, today, unique) get_datetime, getdate, nowdate, today, unique, get_link_to_form)
class DuplicateDeclarationError(frappe.ValidationError): pass class DuplicateDeclarationError(frappe.ValidationError): pass
@ -20,6 +19,7 @@ class EmployeeBoardingController(Document):
Assign to the concerned person and roles as per the onboarding/separation template Assign to the concerned person and roles as per the onboarding/separation template
''' '''
def validate(self): def validate(self):
validate_active_employee(self.employee)
# remove the task if linked before submitting the form # remove the task if linked before submitting the form
if self.amended_from: if self.amended_from:
for activity in self.activities: for activity in self.activities:
@ -522,3 +522,8 @@ def share_doc_with_approver(doc, user):
approver = approvers.get(doc.doctype) approver = approvers.get(doc.doctype)
if doc_before_save.get(approver) != doc.get(approver): if doc_before_save.get(approver) != doc.get(approver):
frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver)) frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver))
def validate_active_employee(employee):
if frappe.db.get_value("Employee", employee, "status") == "Inactive":
frappe.throw(_("Transactions cannot be created for an Inactive Employee {0}.").format(
get_link_to_form("Employee", employee)), InactiveEmployeeStatusError)

View File

@ -83,7 +83,7 @@ frappe.ui.form.on("BOM", {
if (!frm.doc.__islocal && frm.doc.docstatus<2) { if (!frm.doc.__islocal && frm.doc.docstatus<2) {
frm.add_custom_button(__("Update Cost"), function() { frm.add_custom_button(__("Update Cost"), function() {
frm.events.update_cost(frm); frm.events.update_cost(frm, true);
}); });
frm.add_custom_button(__("Browse BOM"), function() { frm.add_custom_button(__("Browse BOM"), function() {
frappe.route_options = { frappe.route_options = {
@ -318,14 +318,15 @@ frappe.ui.form.on("BOM", {
}) })
}, },
update_cost: function(frm) { update_cost: function(frm, save_doc=false) {
return frappe.call({ return frappe.call({
doc: frm.doc, doc: frm.doc,
method: "update_cost", method: "update_cost",
freeze: true, freeze: true,
args: { args: {
update_parent: true, update_parent: true,
from_child_bom:false save: save_doc,
from_child_bom: false
}, },
callback: function(r) { callback: function(r) {
refresh_field("items"); refresh_field("items");

View File

@ -330,7 +330,7 @@ class BOM(WebsiteGenerator):
frappe.get_doc("BOM", bom).update_cost(from_child_bom=True) frappe.get_doc("BOM", bom).update_cost(from_child_bom=True)
if not from_child_bom: if not from_child_bom:
frappe.msgprint(_("Cost Updated")) frappe.msgprint(_("Cost Updated"), alert=True)
def update_parent_cost(self): def update_parent_cost(self):
if self.total_cost: if self.total_cost:
@ -748,7 +748,7 @@ def get_valuation_rate(args):
if valuation_rate <= 0: if valuation_rate <= 0:
last_valuation_rate = frappe.db.sql("""select valuation_rate last_valuation_rate = frappe.db.sql("""select valuation_rate
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where item_code = %s and valuation_rate > 0 where item_code = %s and valuation_rate > 0 and is_cancelled = 0
order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code']) order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code'])
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0 valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0

View File

@ -747,9 +747,8 @@ def get_bin_details(row, company, for_warehouse=None, all_warehouse=False):
group by item_code, warehouse group by item_code, warehouse
""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1) """.format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
def get_warehouse_list(warehouses, warehouse_list=None): def get_warehouse_list(warehouses):
if not warehouse_list: warehouse_list = []
warehouse_list = []
if isinstance(warehouses, str): if isinstance(warehouses, str):
warehouses = json.loads(warehouses) warehouses = json.loads(warehouses)
@ -761,23 +760,19 @@ def get_warehouse_list(warehouses, warehouse_list=None):
else: else:
warehouse_list.append(row.get("warehouse")) warehouse_list.append(row.get("warehouse"))
return warehouse_list
@frappe.whitelist() @frappe.whitelist()
def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None): def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None):
if isinstance(doc, str): if isinstance(doc, str):
doc = frappe._dict(json.loads(doc)) doc = frappe._dict(json.loads(doc))
warehouse_list = []
if warehouses: if warehouses:
get_warehouse_list(warehouses, warehouse_list) warehouses = list(set(get_warehouse_list(warehouses)))
if warehouse_list:
warehouses = list(set(warehouse_list))
if doc.get("for_warehouse") and not get_parent_warehouse_data and doc.get("for_warehouse") in warehouses: if doc.get("for_warehouse") and not get_parent_warehouse_data and doc.get("for_warehouse") in warehouses:
warehouses.remove(doc.get("for_warehouse")) warehouses.remove(doc.get("for_warehouse"))
warehouse_list = None
doc['mr_items'] = [] doc['mr_items'] = []
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items') po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')

View File

@ -10,7 +10,7 @@ from erpnext.stock.doctype.item.test_item import create_item
from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests, get_warehouse_list
class TestProductionPlan(unittest.TestCase): class TestProductionPlan(unittest.TestCase):
def setUp(self): def setUp(self):
@ -251,6 +251,27 @@ class TestProductionPlan(unittest.TestCase):
pln.cancel() pln.cancel()
frappe.delete_doc("Production Plan", pln.name) frappe.delete_doc("Production Plan", pln.name)
def test_get_warehouse_list_group(self):
"""Check if required warehouses are returned"""
warehouse_json = '[{\"warehouse\":\"_Test Warehouse Group - _TC\"}]'
warehouses = set(get_warehouse_list(warehouse_json))
expected_warehouses = {"_Test Warehouse Group-C1 - _TC", "_Test Warehouse Group-C2 - _TC"}
missing_warehouse = expected_warehouses - warehouses
self.assertTrue(len(missing_warehouse) == 0,
msg=f"Following warehouses were expected {', '.join(missing_warehouse)}")
def test_get_warehouse_list_single(self):
warehouse_json = '[{\"warehouse\":\"_Test Scrap Warehouse - _TC\"}]'
warehouses = set(get_warehouse_list(warehouse_json))
expected_warehouses = {"_Test Scrap Warehouse - _TC", }
self.assertEqual(warehouses, expected_warehouses)
def create_production_plan(**args): def create_production_plan(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -487,21 +487,20 @@ class WorkOrder(Document):
return return
operations = [] operations = []
if not self.use_multi_level_bom:
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity") if self.use_multi_level_bom:
operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
else:
bom_tree = frappe.get_doc("BOM", self.bom_no).get_tree_representation() bom_tree = frappe.get_doc("BOM", self.bom_no).get_tree_representation()
bom_traversal = list(reversed(bom_tree.level_order_traversal())) bom_traversal = reversed(bom_tree.level_order_traversal())
bom_traversal.append(bom_tree) # add operation on top level item last
for d in bom_traversal: for node in bom_traversal:
if d.is_bom: if node.is_bom:
operations.extend(_get_operations(d.name, qty=d.exploded_qty)) operations.extend(_get_operations(node.name, qty=node.exploded_qty))
for correct_index, operation in enumerate(operations, start=1): bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
operation.idx = correct_index operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
for correct_index, operation in enumerate(operations, start=1):
operation.idx = correct_index
self.set('operations', operations) self.set('operations', operations)
self.calculate_time() self.calculate_time()

View File

@ -7,6 +7,7 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
from frappe import _, bold from frappe import _, bold
from frappe.utils import getdate, date_diff, comma_and, formatdate from frappe.utils import getdate, date_diff, comma_and, formatdate
from erpnext.hr.utils import validate_active_employee
class AdditionalSalary(Document): class AdditionalSalary(Document):
def on_submit(self): def on_submit(self):
@ -19,6 +20,7 @@ class AdditionalSalary(Document):
self.update_employee_referral(cancel=True) self.update_employee_referral(cancel=True)
def validate(self): def validate(self):
validate_active_employee(self.employee)
self.validate_dates() self.validate_dates()
self.validate_salary_structure() self.validate_salary_structure()
self.validate_recurring_additional_salary_overlap() self.validate_recurring_additional_salary_overlap()

View File

@ -9,10 +9,11 @@ from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount, validate_active_employee
class EmployeeBenefitApplication(Document): class EmployeeBenefitApplication(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
self.validate_duplicate_on_payroll_period() self.validate_duplicate_on_payroll_period()
if not self.max_benefits: if not self.max_benefits:
self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period) self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period)

View File

@ -8,12 +8,13 @@ from frappe import _
from frappe.utils import flt from frappe.utils import flt
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
from erpnext.hr.utils import get_previous_claimed_amount from erpnext.hr.utils import get_previous_claimed_amount, validate_active_employee
from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period
from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
class EmployeeBenefitClaim(Document): class EmployeeBenefitClaim(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
max_benefits = get_max_benefits(self.employee, self.claim_date) max_benefits = get_max_benefits(self.employee, self.claim_date)
if not max_benefits or max_benefits <= 0: if not max_benefits or max_benefits <= 0:
frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee)) frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee))

View File

@ -6,9 +6,11 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import validate_active_employee
class EmployeeIncentive(Document): class EmployeeIncentive(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
self.validate_salary_structure() self.validate_salary_structure()
def validate_salary_structure(self): def validate_salary_structure(self):

View File

@ -8,11 +8,12 @@ from frappe.model.document import Document
from frappe import _ from frappe import _
from frappe.utils import flt from frappe.utils import flt
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \
calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period
class EmployeeTaxExemptionDeclaration(Document): class EmployeeTaxExemptionDeclaration(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
validate_tax_declaration(self.declarations) validate_tax_declaration(self.declarations)
validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
self.set_total_declared_amount() self.set_total_declared_amount()

View File

@ -7,11 +7,12 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
from frappe import _ from frappe import _
from frappe.utils import flt from frappe.utils import flt
from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \
calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period
class EmployeeTaxExemptionProofSubmission(Document): class EmployeeTaxExemptionProofSubmission(Document):
def validate(self): def validate(self):
validate_active_employee(self.employee)
validate_tax_declaration(self.tax_exemption_proofs) validate_tax_declaration(self.tax_exemption_proofs)
self.set_total_actual_amount() self.set_total_actual_amount()
self.set_total_exemption_amount() self.set_total_exemption_amount()

View File

@ -7,11 +7,10 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
from frappe import _ from frappe import _
from frappe.utils import getdate from frappe.utils import getdate
from erpnext.hr.utils import validate_active_employee
class RetentionBonus(Document): class RetentionBonus(Document):
def validate(self): def validate(self):
if frappe.get_value('Employee', self.employee, 'status') != 'Active': validate_active_employee(self.employee)
frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees'))
if getdate(self.bonus_payment_date) < getdate(): if getdate(self.bonus_payment_date) < getdate():
frappe.throw(_('Bonus Payment Date cannot be a past date')) frappe.throw(_('Bonus Payment Date cannot be a past date'))

View File

@ -19,6 +19,7 @@ from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_appli
from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
from erpnext.hr.utils import validate_active_employee
from six import iteritems from six import iteritems
class SalarySlip(TransactionBase): class SalarySlip(TransactionBase):
@ -39,6 +40,7 @@ class SalarySlip(TransactionBase):
def validate(self): def validate(self):
self.status = self.get_status() self.status = self.get_status()
validate_active_employee(self.employee)
self.validate_dates() self.validate_dates()
self.check_existing() self.check_existing()
if not self.salary_slip_based_on_timesheet: if not self.salary_slip_based_on_timesheet:

View File

@ -15,12 +15,15 @@ from erpnext.manufacturing.doctype.workstation.workstation import (check_if_with
WorkstationHolidayError) WorkstationHolidayError)
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
from erpnext.setup.utils import get_exchange_rate from erpnext.setup.utils import get_exchange_rate
from erpnext.hr.utils import validate_active_employee
class OverlapError(frappe.ValidationError): pass class OverlapError(frappe.ValidationError): pass
class OverWorkLoggedError(frappe.ValidationError): pass class OverWorkLoggedError(frappe.ValidationError): pass
class Timesheet(Document): class Timesheet(Document):
def validate(self): def validate(self):
if self.employee:
validate_active_employee(self.employee)
self.set_employee_name() self.set_employee_name()
self.set_status() self.set_status()
self.validate_dates() self.validate_dates()

View File

@ -0,0 +1,16 @@
frappe.ui.form.on("Contact", {
refresh(frm) {
frm.set_query('link_doctype', "links", function() {
return {
query: "frappe.contacts.address_and_contact.filter_dynamic_link_doctypes",
filters: {
fieldtype: ["in", ["HTML", "Text Editor"]],
fieldname: ["in", ["contact_html", "company_description"]],
}
};
});
frm.refresh_field("links");
}
});

View File

@ -162,19 +162,19 @@ def get_batch_qty(batch_no=None, warehouse=None, item_code=None, posting_date=No
out = float(frappe.db.sql("""select sum(actual_qty) out = float(frappe.db.sql("""select sum(actual_qty)
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where warehouse=%s and batch_no=%s {0}""".format(cond), where is_cancelled = 0 and warehouse=%s and batch_no=%s {0}""".format(cond),
(warehouse, batch_no))[0][0] or 0) (warehouse, batch_no))[0][0] or 0)
if batch_no and not warehouse: if batch_no and not warehouse:
out = frappe.db.sql('''select warehouse, sum(actual_qty) as qty out = frappe.db.sql('''select warehouse, sum(actual_qty) as qty
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where batch_no=%s where is_cancelled = 0 and batch_no=%s
group by warehouse''', batch_no, as_dict=1) group by warehouse''', batch_no, as_dict=1)
if not batch_no and item_code and warehouse: if not batch_no and item_code and warehouse:
out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where item_code = %s and warehouse=%s where is_cancelled = 0 and item_code = %s and warehouse=%s
group by batch_no''', (item_code, warehouse), as_dict=1) group by batch_no''', (item_code, warehouse), as_dict=1)
return out return out

View File

@ -239,6 +239,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re
and sle.`item_code`=%(item_code)s and sle.`item_code`=%(item_code)s
and sle.`company` = %(company)s and sle.`company` = %(company)s
and batch.disabled = 0 and batch.disabled = 0
and sle.is_cancelled=0
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
{warehouse_condition} {warehouse_condition}
GROUP BY GROUP BY

View File

@ -1789,7 +1789,7 @@ def get_expired_batch_items():
from `tabBatch` b, `tabStock Ledger Entry` sle from `tabBatch` b, `tabStock Ledger Entry` sle
where b.expiry_date <= %s where b.expiry_date <= %s
and b.expiry_date is not NULL and b.expiry_date is not NULL
and b.batch_id = sle.batch_no and b.batch_id = sle.batch_no and sle.is_cancelled = 0
group by sle.warehouse, sle.item_code, sle.batch_no""",(nowdate()), as_dict=1) group by sle.warehouse, sle.item_code, sle.batch_no""",(nowdate()), as_dict=1)
@frappe.whitelist() @frappe.whitelist()

View File

@ -60,7 +60,7 @@ class StockLedgerEntry(Document):
if self.batch_no and not self.get("allow_negative_stock"): if self.batch_no and not self.get("allow_negative_stock"):
batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty) batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty)
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where warehouse=%s and item_code=%s and batch_no=%s""", where is_cancelled =0 and warehouse=%s and item_code=%s and batch_no=%s""",
(self.warehouse, self.item_code, self.batch_no))[0][0]) (self.warehouse, self.item_code, self.batch_no))[0][0])
if batch_bal_after_transaction < 0: if batch_bal_after_transaction < 0:
@ -152,7 +152,7 @@ class StockLedgerEntry(Document):
last_transaction_time = frappe.db.sql(""" last_transaction_time = frappe.db.sql("""
select MAX(timestamp(posting_date, posting_time)) as posting_time select MAX(timestamp(posting_date, posting_time)) as posting_time
from `tabStock Ledger Entry` from `tabStock Ledger Entry`
where docstatus = 1 and item_code = %s where docstatus = 1 and is_cancelled = 0 and item_code = %s
and warehouse = %s""", (self.item_code, self.warehouse))[0][0] and warehouse = %s""", (self.item_code, self.warehouse))[0][0]
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")

View File

@ -69,7 +69,7 @@ def get_consumed_details(filters):
i.stock_uom, sle.actual_qty, sle.stock_value_difference, i.stock_uom, sle.actual_qty, sle.stock_value_difference,
sle.voucher_no, sle.voucher_type sle.voucher_no, sle.voucher_type
from `tabStock Ledger Entry` sle, `tabItem` i from `tabStock Ledger Entry` sle, `tabItem` i
where sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1): where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1):
consumed_details.setdefault(d.item_code, []).append(d) consumed_details.setdefault(d.item_code, []).append(d)
return consumed_details return consumed_details