Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into gst_sales_register_fix
This commit is contained in:
commit
0011585415
15
.github/helper/semgrep_rules/security.yml
vendored
15
.github/helper/semgrep_rules/security.yml
vendored
@ -8,18 +8,3 @@ rules:
|
||||
dynamic content. Avoid it or use safe_eval().
|
||||
languages: [python]
|
||||
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
|
||||
|
@ -99,7 +99,6 @@ class ReceivablePayableReport(object):
|
||||
voucher_no = gle.voucher_no,
|
||||
party = gle.party,
|
||||
posting_date = gle.posting_date,
|
||||
remarks = gle.remarks,
|
||||
account_currency = gle.account_currency,
|
||||
invoiced = 0.0,
|
||||
paid = 0.0,
|
||||
@ -579,7 +578,7 @@ class ReceivablePayableReport(object):
|
||||
self.gl_entries = frappe.db.sql("""
|
||||
select
|
||||
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
|
||||
`tabGL Entry`
|
||||
where
|
||||
@ -792,8 +791,6 @@ class ReceivablePayableReport(object):
|
||||
self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
|
||||
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):
|
||||
if not fieldname: fieldname = scrub(label)
|
||||
if fieldtype=='Currency': options='currency'
|
||||
|
@ -241,6 +241,7 @@ class GrossProfitGenerator(object):
|
||||
sle.voucher_detail_no == row.item_row:
|
||||
previous_stock_value = len(my_sle) > i+1 and \
|
||||
flt(my_sle[i+1].stock_value) or 0.0
|
||||
|
||||
if previous_stock_value:
|
||||
return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
|
||||
else:
|
||||
@ -335,7 +336,7 @@ class GrossProfitGenerator(object):
|
||||
res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
|
||||
voucher_detail_no, stock_value, warehouse, actual_qty as qty
|
||||
from `tabStock Ledger Entry`
|
||||
where company=%(company)s
|
||||
where company=%(company)s and is_cancelled = 0
|
||||
order by
|
||||
item_code desc, warehouse desc, posting_date desc,
|
||||
posting_time desc, creation desc""", self.filters, as_dict=True)
|
||||
|
@ -1112,8 +1112,11 @@ class AccountsController(TransactionBase):
|
||||
for d in self.get("payment_schedule"):
|
||||
if d.invoice_portion:
|
||||
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
|
||||
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):
|
||||
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
|
||||
|
@ -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
|
||||
where
|
||||
batch.disabled = 0
|
||||
and sle.is_cancelled = 0
|
||||
and sle.item_code = %(item_code)s
|
||||
and sle.warehouse = %(warehouse)s
|
||||
and (sle.batch_no like %(txt)s
|
||||
|
@ -53,12 +53,17 @@ class StockController(AccountsController):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
for d in self.get("items"):
|
||||
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)
|
||||
for serial_no_data in frappe.get_all("Serial No",
|
||||
filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]):
|
||||
if serial_no_data.batch_no != d.batch_no:
|
||||
serial_nos = frappe.get_all("Serial No",
|
||||
fields=["batch_no", "name", "warehouse"],
|
||||
filters={
|
||||
"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}")
|
||||
.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:
|
||||
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
|
||||
|
@ -24,7 +24,8 @@ doctype_js = {
|
||||
"Address": "public/js/address.js",
|
||||
"Communication": "public/js/communication.js",
|
||||
"Event": "public/js/event.js",
|
||||
"Newsletter": "public/js/newsletter.js"
|
||||
"Newsletter": "public/js/newsletter.js",
|
||||
"Contact": "public/js/contact.js"
|
||||
}
|
||||
|
||||
override_doctype_class = {
|
||||
|
@ -9,7 +9,7 @@ from frappe.utils import flt, getdate
|
||||
from frappe import _
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
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):
|
||||
def validate(self):
|
||||
@ -19,6 +19,7 @@ class Appraisal(Document):
|
||||
if not self.goals:
|
||||
frappe.throw(_("Goals cannot be empty"))
|
||||
|
||||
validate_active_employee(self.employee)
|
||||
set_employee_name(self)
|
||||
self.validate_dates()
|
||||
self.validate_existing_appraisal()
|
||||
|
@ -8,11 +8,13 @@ from frappe.utils import getdate, nowdate
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr, get_datetime, formatdate
|
||||
from erpnext.hr.utils import validate_active_employee
|
||||
|
||||
class Attendance(Document):
|
||||
def validate(self):
|
||||
from erpnext.controllers.status_updater import validate_status
|
||||
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_attendance_date()
|
||||
self.validate_duplicate_record()
|
||||
self.validate_employee_status()
|
||||
|
@ -8,10 +8,11 @@ from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import date_diff, add_days, getdate
|
||||
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):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
validate_dates(self, self.from_date, self.to_date)
|
||||
if self.half_day:
|
||||
if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date):
|
||||
|
@ -7,12 +7,13 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import date_diff, add_days, getdate, cint, format_date
|
||||
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
|
||||
|
||||
class CompensatoryLeaveRequest(Document):
|
||||
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
validate_dates(self, self.work_from_date, self.work_end_date)
|
||||
if self.half_day:
|
||||
if not self.half_day_date:
|
||||
|
@ -13,8 +13,10 @@ from frappe.model.document import Document
|
||||
from erpnext.utilities.transaction_base import delete_events
|
||||
from frappe.utils.nestedset import NestedSet
|
||||
|
||||
class EmployeeUserDisabledError(frappe.ValidationError): pass
|
||||
class EmployeeLeftValidationError(frappe.ValidationError): pass
|
||||
class EmployeeUserDisabledError(frappe.ValidationError):
|
||||
pass
|
||||
class InactiveEmployeeStatusError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
class Employee(NestedSet):
|
||||
nsm_parent_field = 'reports_to'
|
||||
@ -196,7 +198,7 @@ class Employee(NestedSet):
|
||||
message += "<br><br><ul><li>" + "</li><li>".join(link_to_employees)
|
||||
message += "</li></ul><br>"
|
||||
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:
|
||||
throw(_("Please enter relieving date."))
|
||||
|
||||
|
@ -7,7 +7,7 @@ import frappe
|
||||
import erpnext
|
||||
import unittest
|
||||
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')
|
||||
|
||||
@ -45,10 +45,33 @@ class TestEmployee(unittest.TestCase):
|
||||
employee2_doc.save()
|
||||
employee1_doc.reload()
|
||||
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):
|
||||
""
|
||||
if not frappe.db.get_value("User", user):
|
||||
frappe.get_doc({
|
||||
"doctype": "User",
|
||||
@ -80,4 +103,5 @@ def make_employee(user, company=None, **kwargs):
|
||||
employee.insert()
|
||||
return employee.name
|
||||
else:
|
||||
frappe.db.set_value("Employee", {"employee_name":user}, "status", "Active")
|
||||
return frappe.get_value("Employee", {"employee_name":user}, "name")
|
||||
|
@ -8,6 +8,7 @@ from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import flt, nowdate
|
||||
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):
|
||||
pass
|
||||
@ -18,6 +19,7 @@ class EmployeeAdvance(Document):
|
||||
'make_payment_via_journal_entry')
|
||||
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.set_status()
|
||||
|
||||
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)
|
||||
if not bank_cash_account:
|
||||
frappe.throw(_("Please set a Default Cash Account in Company defaults"))
|
||||
|
||||
|
||||
advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency')
|
||||
|
||||
|
||||
je = frappe.new_doc('Journal Entry')
|
||||
je.posting_date = nowdate()
|
||||
je.voucher_type = get_voucher_type(mode_of_payment)
|
||||
|
@ -9,9 +9,11 @@ from frappe.model.document import Document
|
||||
from frappe import _
|
||||
|
||||
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):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_duplicate_log()
|
||||
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):
|
||||
"""Given a set of logs in chronological order calculates the total working hours based on the parameters.
|
||||
Zero is returned for all invalid cases.
|
||||
|
||||
|
||||
: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 working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out'
|
||||
|
@ -7,12 +7,11 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
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):
|
||||
def validate(self):
|
||||
if frappe.get_value("Employee", self.employee, "status") != "Active":
|
||||
frappe.throw(_("Cannot promote Employee with status Left or Inactive"))
|
||||
validate_active_employee(self.employee)
|
||||
|
||||
def before_submit(self):
|
||||
if getdate(self.promotion_date) > getdate():
|
||||
|
@ -7,9 +7,11 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import get_link_to_form
|
||||
from frappe.model.document import Document
|
||||
from erpnext.hr.utils import validate_active_employee
|
||||
|
||||
class EmployeeReferral(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.referrer)
|
||||
self.set_full_name()
|
||||
self.set_referral_bonus_payment_status()
|
||||
|
||||
|
@ -10,10 +10,6 @@ from frappe.utils import getdate
|
||||
from erpnext.hr.utils import update_employee
|
||||
|
||||
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):
|
||||
if getdate(self.transfer_date) > getdate():
|
||||
frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"),
|
||||
|
@ -6,7 +6,7 @@ import frappe, erpnext
|
||||
from frappe import _
|
||||
from frappe.utils import get_fullname, flt, cstr, get_link_to_form
|
||||
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.general_ledger import make_gl_entries
|
||||
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')
|
||||
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_advances()
|
||||
self.validate_sanctioned_amount()
|
||||
self.calculate_total_amount()
|
||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
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.employee.employee import get_holiday_list_for_employee
|
||||
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)
|
||||
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
set_employee_name(self)
|
||||
self.validate_dates()
|
||||
self.validate_balance_leaves()
|
||||
|
@ -7,7 +7,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
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.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
|
||||
@ -15,6 +15,7 @@ from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leav
|
||||
class LeaveEncashment(Document):
|
||||
def validate(self):
|
||||
set_employee_name(self)
|
||||
validate_active_employee(self.employee)
|
||||
self.get_leave_details_for_encashment()
|
||||
self.validate_salary_structure()
|
||||
|
||||
|
@ -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 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.utils import validate_active_employee
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
class ShiftAssignment(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_overlapping_dates()
|
||||
|
||||
if self.end_date and self.end_date <= self.start_date:
|
||||
|
@ -7,12 +7,13 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
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 ShiftRequest(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_dates()
|
||||
self.validate_shift_request_overlap_dates()
|
||||
self.validate_approver()
|
||||
|
@ -5,6 +5,8 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from erpnext.hr.utils import validate_active_employee
|
||||
|
||||
class TravelRequest(Document):
|
||||
pass
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
|
@ -3,13 +3,12 @@
|
||||
|
||||
import erpnext
|
||||
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.desk.form import assign_to
|
||||
from frappe.model.document import Document
|
||||
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
|
||||
|
||||
@ -20,6 +19,7 @@ class EmployeeBoardingController(Document):
|
||||
Assign to the concerned person and roles as per the onboarding/separation template
|
||||
'''
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
# remove the task if linked before submitting the form
|
||||
if self.amended_from:
|
||||
for activity in self.activities:
|
||||
@ -522,3 +522,8 @@ def share_doc_with_approver(doc, user):
|
||||
approver = approvers.get(doc.doctype)
|
||||
if doc_before_save.get(approver) != doc.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)
|
@ -83,7 +83,7 @@ frappe.ui.form.on("BOM", {
|
||||
|
||||
if (!frm.doc.__islocal && frm.doc.docstatus<2) {
|
||||
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() {
|
||||
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({
|
||||
doc: frm.doc,
|
||||
method: "update_cost",
|
||||
freeze: true,
|
||||
args: {
|
||||
update_parent: true,
|
||||
from_child_bom:false
|
||||
save: save_doc,
|
||||
from_child_bom: false
|
||||
},
|
||||
callback: function(r) {
|
||||
refresh_field("items");
|
||||
|
@ -330,7 +330,7 @@ class BOM(WebsiteGenerator):
|
||||
frappe.get_doc("BOM", bom).update_cost(from_child_bom=True)
|
||||
|
||||
if not from_child_bom:
|
||||
frappe.msgprint(_("Cost Updated"))
|
||||
frappe.msgprint(_("Cost Updated"), alert=True)
|
||||
|
||||
def update_parent_cost(self):
|
||||
if self.total_cost:
|
||||
@ -748,7 +748,7 @@ def get_valuation_rate(args):
|
||||
if valuation_rate <= 0:
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
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'])
|
||||
|
||||
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
|
||||
|
@ -747,9 +747,8 @@ def get_bin_details(row, company, for_warehouse=None, all_warehouse=False):
|
||||
group by item_code, warehouse
|
||||
""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
|
||||
|
||||
def get_warehouse_list(warehouses, warehouse_list=None):
|
||||
if not warehouse_list:
|
||||
warehouse_list = []
|
||||
def get_warehouse_list(warehouses):
|
||||
warehouse_list = []
|
||||
|
||||
if isinstance(warehouses, str):
|
||||
warehouses = json.loads(warehouses)
|
||||
@ -761,23 +760,19 @@ def get_warehouse_list(warehouses, warehouse_list=None):
|
||||
else:
|
||||
warehouse_list.append(row.get("warehouse"))
|
||||
|
||||
return warehouse_list
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None):
|
||||
if isinstance(doc, str):
|
||||
doc = frappe._dict(json.loads(doc))
|
||||
|
||||
warehouse_list = []
|
||||
if warehouses:
|
||||
get_warehouse_list(warehouses, warehouse_list)
|
||||
|
||||
if warehouse_list:
|
||||
warehouses = list(set(warehouse_list))
|
||||
warehouses = list(set(get_warehouse_list(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"))
|
||||
|
||||
warehouse_list = None
|
||||
|
||||
doc['mr_items'] = []
|
||||
|
||||
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
|
||||
|
@ -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.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.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):
|
||||
def setUp(self):
|
||||
@ -251,6 +251,27 @@ class TestProductionPlan(unittest.TestCase):
|
||||
pln.cancel()
|
||||
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):
|
||||
args = frappe._dict(args)
|
||||
|
||||
|
@ -487,21 +487,20 @@ class WorkOrder(Document):
|
||||
return
|
||||
|
||||
operations = []
|
||||
if not self.use_multi_level_bom:
|
||||
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
|
||||
operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty))
|
||||
else:
|
||||
|
||||
if self.use_multi_level_bom:
|
||||
bom_tree = frappe.get_doc("BOM", self.bom_no).get_tree_representation()
|
||||
bom_traversal = list(reversed(bom_tree.level_order_traversal()))
|
||||
bom_traversal.append(bom_tree) # add operation on top level item last
|
||||
bom_traversal = reversed(bom_tree.level_order_traversal())
|
||||
|
||||
for d in bom_traversal:
|
||||
if d.is_bom:
|
||||
operations.extend(_get_operations(d.name, qty=d.exploded_qty))
|
||||
for node in bom_traversal:
|
||||
if node.is_bom:
|
||||
operations.extend(_get_operations(node.name, qty=node.exploded_qty))
|
||||
|
||||
for correct_index, operation in enumerate(operations, start=1):
|
||||
operation.idx = correct_index
|
||||
bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity")
|
||||
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.calculate_time()
|
||||
|
@ -7,6 +7,7 @@ import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe import _, bold
|
||||
from frappe.utils import getdate, date_diff, comma_and, formatdate
|
||||
from erpnext.hr.utils import validate_active_employee
|
||||
|
||||
class AdditionalSalary(Document):
|
||||
def on_submit(self):
|
||||
@ -19,6 +20,7 @@ class AdditionalSalary(Document):
|
||||
self.update_employee_referral(cancel=True)
|
||||
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_dates()
|
||||
self.validate_salary_structure()
|
||||
self.validate_recurring_additional_salary_overlap()
|
||||
|
@ -9,10 +9,11 @@ from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
|
||||
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.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):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_duplicate_on_payroll_period()
|
||||
if not self.max_benefits:
|
||||
self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period)
|
||||
|
@ -8,12 +8,13 @@ from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.model.document import Document
|
||||
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.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
|
||||
|
||||
class EmployeeBenefitClaim(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
max_benefits = get_max_benefits(self.employee, self.claim_date)
|
||||
if not max_benefits or max_benefits <= 0:
|
||||
frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee))
|
||||
|
@ -6,9 +6,11 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.hr.utils import validate_active_employee
|
||||
|
||||
class EmployeeIncentive(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_salary_structure()
|
||||
|
||||
def validate_salary_structure(self):
|
||||
|
@ -8,11 +8,12 @@ from frappe.model.document import Document
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
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
|
||||
|
||||
class EmployeeTaxExemptionDeclaration(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
validate_tax_declaration(self.declarations)
|
||||
validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee)
|
||||
self.set_total_declared_amount()
|
||||
|
@ -7,11 +7,12 @@ import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
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
|
||||
|
||||
class EmployeeTaxExemptionProofSubmission(Document):
|
||||
def validate(self):
|
||||
validate_active_employee(self.employee)
|
||||
validate_tax_declaration(self.tax_exemption_proofs)
|
||||
self.set_total_actual_amount()
|
||||
self.set_total_exemption_amount()
|
||||
|
@ -7,11 +7,10 @@ import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
from frappe.utils import getdate
|
||||
|
||||
from erpnext.hr.utils import validate_active_employee
|
||||
class RetentionBonus(Document):
|
||||
def validate(self):
|
||||
if frappe.get_value('Employee', self.employee, 'status') != 'Active':
|
||||
frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees'))
|
||||
validate_active_employee(self.employee)
|
||||
if getdate(self.bonus_payment_date) < getdate():
|
||||
frappe.throw(_('Bonus Payment Date cannot be a past date'))
|
||||
|
||||
|
@ -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.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.hr.utils import validate_active_employee
|
||||
from six import iteritems
|
||||
|
||||
class SalarySlip(TransactionBase):
|
||||
@ -39,6 +40,7 @@ class SalarySlip(TransactionBase):
|
||||
|
||||
def validate(self):
|
||||
self.status = self.get_status()
|
||||
validate_active_employee(self.employee)
|
||||
self.validate_dates()
|
||||
self.check_existing()
|
||||
if not self.salary_slip_based_on_timesheet:
|
||||
|
@ -15,12 +15,15 @@ from erpnext.manufacturing.doctype.workstation.workstation import (check_if_with
|
||||
WorkstationHolidayError)
|
||||
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.hr.utils import validate_active_employee
|
||||
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
class OverWorkLoggedError(frappe.ValidationError): pass
|
||||
|
||||
class Timesheet(Document):
|
||||
def validate(self):
|
||||
if self.employee:
|
||||
validate_active_employee(self.employee)
|
||||
self.set_employee_name()
|
||||
self.set_status()
|
||||
self.validate_dates()
|
||||
|
16
erpnext/public/js/contact.js
Normal file
16
erpnext/public/js/contact.js
Normal 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");
|
||||
}
|
||||
});
|
@ -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)
|
||||
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)
|
||||
|
||||
if batch_no and not warehouse:
|
||||
out = frappe.db.sql('''select warehouse, sum(actual_qty) as qty
|
||||
from `tabStock Ledger Entry`
|
||||
where batch_no=%s
|
||||
where is_cancelled = 0 and batch_no=%s
|
||||
group by warehouse''', batch_no, as_dict=1)
|
||||
|
||||
if not batch_no and item_code and warehouse:
|
||||
out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty
|
||||
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)
|
||||
|
||||
return out
|
||||
|
@ -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.`company` = %(company)s
|
||||
and batch.disabled = 0
|
||||
and sle.is_cancelled=0
|
||||
and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s
|
||||
{warehouse_condition}
|
||||
GROUP BY
|
||||
|
@ -1789,7 +1789,7 @@ def get_expired_batch_items():
|
||||
from `tabBatch` b, `tabStock Ledger Entry` sle
|
||||
where b.expiry_date <= %s
|
||||
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)
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -60,7 +60,7 @@ class StockLedgerEntry(Document):
|
||||
if self.batch_no and not self.get("allow_negative_stock"):
|
||||
batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty)
|
||||
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])
|
||||
|
||||
if batch_bal_after_transaction < 0:
|
||||
@ -152,7 +152,7 @@ class StockLedgerEntry(Document):
|
||||
last_transaction_time = frappe.db.sql("""
|
||||
select MAX(timestamp(posting_date, posting_time)) as posting_time
|
||||
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]
|
||||
|
||||
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
|
||||
|
@ -69,7 +69,7 @@ def get_consumed_details(filters):
|
||||
i.stock_uom, sle.actual_qty, sle.stock_value_difference,
|
||||
sle.voucher_no, sle.voucher_type
|
||||
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)
|
||||
|
||||
return consumed_details
|
||||
|
Loading…
x
Reference in New Issue
Block a user