def validate_service_stop_date(doc): ''' Validates service_stop_date for Purchase Invoice and Sales Invoice ''' enable_check = "enable_deferred_revenue" \ if doc.doctype=="Sales Invoice" else "enable_deferred_expense" old_stop_dates = {} old_doc = dataent.db.get_all("{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]) for d in old_doc: old_stop_dates[d.name] = d.service_stop_date or "" for item in doc.items: if not item.get(enable_check): continue if item.service_stop_date: if date_diff(item.service_stop_date, item.service_start_date) < 0: dataent.throw(_("Service Stop Date cannot be before Service Start Date")) if date_diff(item.service_stop_date, item.service_end_date) > 0: dataent.throw(_("Service Stop Date cannot be after Service End Date")) if old_stop_dates and old_stop_dates.get(item.name) and item.service_stop_date!=old_stop_dates[item.name]: dataent.throw(_("Cannot change Service Stop Date for item in row {0}".format(item.idx)))
def get_period_factor(employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0): # TODO if both deduct checked update the factor to make tax consistent period_start, period_end = payroll_period.start_date, payroll_period.end_date joining_date, relieving_date = dataent.db.get_value( "Employee", employee, ["date_of_joining", "relieving_date"]) if getdate(joining_date) > getdate(period_start): period_start = joining_date if relieving_date and getdate(relieving_date) < getdate(period_end): period_end = relieving_date total_sub_periods, remaining_sub_periods = 0.0, 0.0 if payroll_frequency == "Monthly" and not depends_on_payment_days: total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date) remaining_sub_periods = month_diff(period_end, start_date) else: salary_days = date_diff(end_date, start_date) + 1 days_in_payroll_period = date_diff(payroll_period.end_date, payroll_period.start_date) + 1 total_sub_periods = flt(days_in_payroll_period) / flt(salary_days) remaining_days_in_payroll_period = date_diff(period_end, start_date) + 1 remaining_sub_periods = flt(remaining_days_in_payroll_period) / flt( salary_days) return total_sub_periods, remaining_sub_periods
def get_amount(self, sal_start_date, sal_end_date): start_date = getdate(sal_start_date) end_date = getdate(sal_end_date) total_days = date_diff(getdate(self.to_date), getdate( self.from_date)) + 1 amount_per_day = self.amount / total_days if getdate(sal_start_date) <= getdate(self.from_date): start_date = getdate(self.from_date) if getdate(sal_end_date) > getdate(self.to_date): end_date = getdate(self.to_date) no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1 return amount_per_day * no_of_days
def filter_transactions_data(self, consolidated=False): sales_orders = [d.sales_order for d in self.transactions] doctype = "Sales Order" filters = {'name': ('in', sales_orders)} if not consolidated: sales_order_items = [d.so_detail for d in self.transactions] doctype = "Sales Order Item" filters = { 'parent': ('in', sales_orders), 'name': ('in', sales_order_items) } so_data = {} for d in dataent.get_all(doctype, filters=filters, fields=["delivery_date", "parent", "name"]): key = d.name if consolidated else (d.parent, d.name) if key not in so_data: so_data.setdefault(key, d.delivery_date) for row in self.transactions: key = row.sales_order if consolidated else (row.sales_order, row.so_detail) row.update({ 'delivery_date': so_data.get(key), 'delayed_days': date_diff(row.posting_date, so_data.get(key)) }) return self.transactions
def execute(filters=None): if not filters: filters = {} from_date = get_first_day(filters["month"] + '-' + filters["year"]) to_date = get_last_day(filters["month"] + '-' + filters["year"]) total_days_in_month = date_diff(to_date, from_date) +1 columns = get_columns(total_days_in_month) students = get_student_group_students(filters.get("student_group"),1) students_list = get_students_list(students) att_map = get_attendance_list(from_date, to_date, filters.get("student_group"), students_list) data = [] for stud in students: row = [stud.student, stud.student_name] student_status = dataent.db.get_value("Student", stud.student, "enabled") date = from_date total_p = total_a = 0.0 for day in range(total_days_in_month): status="None" if att_map.get(stud.student): status = att_map.get(stud.student).get(date, "None") elif not student_status: status = "Inactive" else: status = "None" status_map = {"Present": "P", "Absent": "A", "None": "", "Inactive":"-"} row.append(status_map[status]) if status == "Present": total_p += 1 elif status == "Absent": total_a += 1 date = add_days(date, 1) row += [total_p, total_a] data.append(row) return columns, data
def get_payment_days(self, joining_date, relieving_date): start_date = getdate(self.start_date) if joining_date: if getdate(self.start_date) <= joining_date <= getdate( self.end_date): start_date = joining_date elif joining_date > getdate(self.end_date): return end_date = getdate(self.end_date) if relieving_date: if getdate(self.start_date) <= relieving_date <= getdate( self.end_date): end_date = relieving_date elif relieving_date < getdate(self.start_date): dataent.throw( _("Employee relieved on {0} must be set as 'Left'").format( relieving_date)) payment_days = date_diff(end_date, start_date) + 1 if not cint( dataent.db.get_value( "HR Settings", None, "include_holidays_in_total_working_days")): holidays = self.get_holidays_for_employee(start_date, end_date) payment_days -= len(holidays) return payment_days
def validate_house_rent_dates(doc): if not doc.rented_to_date or not doc.rented_from_date: dataent.throw( _("House rented dates required for exemption calculation")) if date_diff(doc.rented_to_date, doc.rented_from_date) < 14: dataent.throw(_("House rented dates should be atleast 15 days apart")) proofs = dataent.db.sql( """ select name from `tabEmployee Tax Exemption Proof Submission` where docstatus=1 and employee=%(employee)s and payroll_period=%(payroll_period)s and (rented_from_date between %(from_date)s and %(to_date)s or rented_to_date between %(from_date)s and %(to_date)s) """, { "employee": doc.employee, "payroll_period": doc.payroll_period, "from_date": doc.rented_from_date, "to_date": doc.rented_to_date }) if proofs: dataent.throw( _("House rent paid days overlapping with {0}").format( proofs[0][0]))
def validate_due_date(posting_date, due_date, party_type, party, company=None, bill_date=None, template_name=None): if getdate(due_date) < getdate(posting_date): dataent.throw( _("Due Date cannot be before Posting / Supplier Invoice Date")) else: if not template_name: return default_due_date = get_due_date_from_template( template_name, posting_date, bill_date).strftime("%Y-%m-%d") if not default_due_date: return if default_due_date != posting_date and getdate(due_date) > getdate( default_due_date): is_credit_controller = dataent.db.get_single_value( "Accounts Settings", "credit_controller") in dataent.get_roles() if is_credit_controller: msgprint( _("Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s)" ).format(date_diff(due_date, default_due_date))) else: dataent.throw( _("Due / Reference Date cannot be after {0}").format( formatdate(default_due_date)))
def get_payroll_period_days(start_date, end_date, employee): company = dataent.db.get_value("Employee", employee, "company") payroll_period = dataent.db.sql( """ select name, start_date, end_date from `tabPayroll Period` where company=%(company)s and %(start_date)s between start_date and end_date and %(end_date)s between start_date and end_date """, { 'company': company, 'start_date': start_date, 'end_date': end_date }) if len(payroll_period) > 0: actual_no_of_days = date_diff(getdate(payroll_period[0][2]), getdate(payroll_period[0][1])) + 1 working_days = actual_no_of_days if not cint( dataent.db.get_value( "HR Settings", None, "include_holidays_in_total_working_days")): holidays = get_holidays_for_employee(employee, getdate(payroll_period[0][1]), getdate(payroll_period[0][2])) working_days -= len(holidays) return payroll_period[0][0], working_days, actual_no_of_days return False, False, False
def make_time_logs(self, open_new=False): """Capacity Planning. Plan time logs based on earliest availablity of workstation after Planned Start Date. Time logs will be created and remain in Draft mode and must be submitted before manufacturing entry can be made.""" if not self.operations: return timesheets = [] plan_days = dataent.db.get_single_value( "Manufacturing Settings", "capacity_planning_for_days") or 30 timesheet = make_timesheet(self.name, self.company) timesheet.set('time_logs', []) for i, d in enumerate(self.operations): if d.status != 'Completed': self.set_start_end_time_for_workstation(d, i) args = self.get_operations_data(d) add_timesheet_detail(timesheet, args) original_start_time = d.planned_start_time # validate operating hours if workstation [not mandatory] is specified try: timesheet.validate_time_logs() except OverlapError: if dataent.message_log: dataent.message_log.pop() timesheet.schedule_for_production_order(d.idx) except WorkstationHolidayError: if dataent.message_log: dataent.message_log.pop() timesheet.schedule_for_production_order(d.idx) from_time, to_time = self.get_start_end_time(timesheet, d.name) if date_diff(from_time, original_start_time) > cint(plan_days): dataent.throw( _("Unable to find Time Slot in the next {0} days for Operation {1}" ).format(plan_days, d.operation)) break d.planned_start_time = from_time d.planned_end_time = to_time d.db_update() if timesheet and open_new: return timesheet if timesheet and timesheet.get("time_logs"): timesheet.save() timesheets.append(getlink("Timesheet", timesheet.name)) self.planned_end_date = self.operations[-1].planned_end_time if timesheets: dataent.local.message_log = [] dataent.msgprint( _("Timesheet created:") + "\n" + "\n".join(timesheets))
def get_average_age(fifo_queue, to_date): batch_age = age_qty = total_qty = 0.0 for batch in fifo_queue: batch_age = date_diff(to_date, batch[1]) age_qty += batch_age * batch[0] total_qty += batch[0] return (age_qty / total_qty) if total_qty else 0.0
def validate_holidays(self): holidays = get_holidays_for_employee(self.employee, self.work_from_date, self.work_end_date) if len(holidays) < date_diff(self.work_end_date, self.work_from_date) + 1: dataent.throw( _("Compensatory leave request days not in valid holidays"))
def reschedule_depreciations(self, asset_value): asset = dataent.get_doc('Asset', self.asset) for d in asset.finance_books: d.value_after_depreciation = asset_value if d.depreciation_method in ("Straight Line", "Manual"): end_date = max([ s.schedule_date for s in asset.schedules if cint(s.finance_book_id) == d.idx ]) total_days = date_diff(end_date, self.date) rate_per_day = flt( d.value_after_depreciation) / flt(total_days) from_date = self.date else: no_of_depreciations = len([ s.name for s in asset.schedules if (cint(s.finance_book_id) == d.idx and not s.journal_entry) ]) value_after_depreciation = d.value_after_depreciation for data in asset.schedules: if cint(data.finance_book_id ) == d.idx and not data.journal_entry: if d.depreciation_method in ("Straight Line", "Manual"): days = date_diff(data.schedule_date, from_date) depreciation_amount = days * rate_per_day from_date = data.schedule_date else: depreciation_amount = asset.get_depreciation_amount( value_after_depreciation, no_of_depreciations, d) if depreciation_amount: value_after_depreciation -= flt(depreciation_amount) data.depreciation_amount = depreciation_amount d.db_update() asset.set_accumulated_depreciation(ignore_booked_entry=True) for asset_data in asset.schedules: if not asset_data.journal_entry: asset_data.db_update()
def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day=None, half_day_date=None): number_of_days = 0 if cint(half_day) == 1: if from_date == to_date: number_of_days = 0.5 else: number_of_days = date_diff(to_date, from_date) + .5 else: number_of_days = date_diff(to_date, from_date) + 1 if not dataent.db.get_value("Leave Type", leave_type, "include_holiday"): number_of_days = flt(number_of_days) - flt( get_holidays(employee, from_date, to_date)) return number_of_days
def get_data(filters): out = [] for room_type in dataent.get_all('Hotel Room Type'): total_booked = 0 for i in range(date_diff(filters.to_date, filters.from_date)): day = add_days(filters.from_date, i) total_booked += get_rooms_booked(room_type.name, day) out.append([room_type.name, total_booked]) return out
def create_attendance(self): request_days = date_diff(self.to_date, self.from_date) + 1 for number in range(request_days): attendance_date = add_days(self.from_date, number) skip_attendance = self.validate_if_attendance_not_applicable( attendance_date) if not skip_attendance: attendance = dataent.new_doc("Attendance") attendance.employee = self.employee attendance.employee_name = self.employee_name if self.half_day and date_diff(getdate(self.half_day_date), getdate(attendance_date)) == 0: attendance.status = "Half Day" else: attendance.status = "Present" attendance.attendance_date = attendance_date attendance.company = self.company attendance.attendance_request = self.name attendance.save(ignore_permissions=True) attendance.submit()
def calculate_std_hours(self): std_working_hours = dataent.get_value("Company", self.company, 'standard_working_hours') for time in self.time_logs: if time.from_time and time.to_time: if flt(std_working_hours) > 0: time.hours = flt(std_working_hours) * date_diff( time.to_time, time.from_time) else: if not time.hours: time.hours = time_diff_in_hours( time.to_time, time.from_time)
def on_cancel(self): if self.leave_allocation: date_difference = date_diff(self.work_end_date, self.work_from_date) + 1 if self.half_day: date_difference -= 0.5 leave_allocation = dataent.get_doc("Leave Allocation", self.leave_allocation) if leave_allocation: leave_allocation.new_leaves_allocated -= date_difference if leave_allocation.total_leaves_allocated - date_difference <= 0: leave_allocation.total_leaves_allocated = 0 leave_allocation.submit()
def execute(filters=None): columns = get_columns() item_details = get_fifo_queue(filters) to_date = filters["to_date"] data = [] for item, item_dict in iteritems(item_details): fifo_queue = item_dict["fifo_queue"] details = item_dict["details"] if not fifo_queue: continue average_age = get_average_age(fifo_queue, to_date) earliest_age = date_diff(to_date, fifo_queue[0][1]) latest_age = date_diff(to_date, fifo_queue[-1][1]) data.append([ item, details.item_name, details.description, details.item_group, details.brand, average_age, earliest_age, latest_age, details.stock_uom ]) return columns, data
def _book_deferred_revenue_or_expense(item): start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date) if not (start_date and end_date): return account_currency = get_account_currency(item.expense_account) if doc.doctype == "Sales Invoice": against, project = doc.customer, doc.project credit_account, debit_account = item.income_account, item.deferred_revenue_account else: against, project = doc.supplier, item.project credit_account, debit_account = item.deferred_expense_account, item.expense_account total_days = date_diff(item.service_end_date, item.service_start_date) + 1 total_booking_days = date_diff(end_date, start_date) + 1 amount, base_amount = calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency) make_gl_entries(doc, credit_account, debit_account, against, amount, base_amount, end_date, project, account_currency, item.cost_center, item.name) if getdate(end_date) < getdate(posting_date) and not last_gl_entry: _book_deferred_revenue_or_expense(item)
def get_communication_details(filters): communication_count = None communication_list = [] opportunities = dataent.db.get_values('Opportunity', {'opportunity_from': 'Lead'},\ ['name', 'customer_name', 'lead', 'contact_email'], as_dict=1) for d in opportunities: invoice = dataent.db.sql(''' SELECT date(creation) FROM `tabSales Invoice` WHERE contact_email = %s AND date(creation) between %s and %s AND docstatus != 2 ORDER BY creation LIMIT 1 ''', (d.contact_email, filters.from_date, filters.to_date)) if not invoice: continue communication_count = dataent.db.sql(''' SELECT count(*) FROM `tabCommunication` WHERE sender = %s AND date(communication_date) <= %s ''', (d.contact_email, invoice))[0][0] if not communication_count: continue first_contact = dataent.db.sql(''' SELECT date(communication_date) FROM `tabCommunication` WHERE recipients = %s ORDER BY communication_date LIMIT 1 ''', (d.contact_email))[0][0] duration = flt(date_diff(invoice[0][0], first_contact)) support_tickets = len(dataent.db.get_all('Issue', {'raised_by': d.contact_email})) communication_list.append({'customer': d.customer_name, 'interactions': communication_count, 'duration': duration, 'support_tickets': support_tickets}) return communication_list
def validate_employee_attendance(self): employees_to_mark_attendance = [] days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0 for employee_detail in self.employees: days_holiday = self.get_count_holidays_of_employee( employee_detail.employee) days_attendance_marked = self.get_count_employee_attendance( employee_detail.employee) days_in_payroll = date_diff(self.end_date, self.start_date) + 1 if days_in_payroll > days_holiday + days_attendance_marked: employees_to_mark_attendance.append({ "employee": employee_detail.employee, "employee_name": employee_detail.employee_name }) return employees_to_mark_attendance
def validate_attendance(self): query = """select attendance_date, status from `tabAttendance` where attendance_date between %(work_from_date)s and %(work_end_date)s and docstatus=1 and status = 'Present' and employee=%(employee)s""" attendance = dataent.db.sql(query, { "work_from_date": self.work_from_date, "work_end_date": self.work_end_date, "employee": self.employee }, as_dict=True) if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1: dataent.throw( _("You are not present all day(s) between compensatory leave request days" ))
def send_reminder(): dataent.has_permission('GST Settings', throw=True) last_sent = dataent.db.get_single_value('GST Settings', 'gstin_email_sent_on') if last_sent and date_diff(nowdate(), last_sent) < 3: dataent.throw(_("Please wait 3 days before resending the reminder.")) dataent.db.set_value('GST Settings', 'GST Settings', 'gstin_email_sent_on', nowdate()) # enqueue if large number of customers, suppliser dataent.enqueue( 'epaas.regional.doctype.gst_settings.gst_settings.send_gstin_reminder_to_all_parties' ) dataent.msgprint( _('Email Reminders will be sent to all parties with email contacts'))
def get_max_benefits_remaining(employee, on_date, payroll_period): max_benefits = get_max_benefits(employee, on_date) if max_benefits and max_benefits > 0: have_depends_on_payment_days = False per_day_amount_total = 0 payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[0] payroll_period_obj = dataent.get_doc("Payroll Period", payroll_period) # Get all salary slip flexi amount in the payroll period prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given( employee, payroll_period_obj) if prev_sal_slip_flexi_total > 0: # Check salary structure hold depends_on_payment_days component # If yes then find the amount per day of each component and find the sum sal_struct_name = get_assigned_salary_structure(employee, on_date) if sal_struct_name: sal_struct = dataent.get_doc("Salary Structure", sal_struct_name) for sal_struct_row in sal_struct.get("earnings"): salary_component = dataent.get_doc( "Salary Component", sal_struct_row.salary_component) if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1: have_depends_on_payment_days = True benefit_amount = get_benefit_amount_based_on_pro_rata( sal_struct, salary_component.max_benefit_amount) amount_per_day = benefit_amount / payroll_period_days per_day_amount_total += amount_per_day # Then the sum multiply with the no of lwp in that period # Include that amount to the prev_sal_slip_flexi_total to get the actual if have_depends_on_payment_days and per_day_amount_total > 0: holidays = get_holidays_for_employee( employee, payroll_period_obj.start_date, on_date) working_days = date_diff(on_date, payroll_period_obj.start_date) + 1 leave_days = calculate_lwp(employee, payroll_period_obj.start_date, holidays, working_days) leave_days_amount = leave_days * per_day_amount_total prev_sal_slip_flexi_total += leave_days_amount return max_benefits - prev_sal_slip_flexi_total return max_benefits
def calculate_hra_exemption_for_period(doc): monthly_rent, eligible_hra = 0, 0 if doc.house_rent_payment_amount: validate_house_rent_dates(doc) # TODO receive rented months or validate dates are start and end of months? # Calc monthly rent, round to nearest .5 factor = flt(date_diff(doc.rented_to_date, doc.rented_from_date) + 1) / 30 factor = round(factor * 2) / 2 monthly_rent = doc.house_rent_payment_amount / factor # update field used by calculate_annual_eligible_hra_exemption doc.monthly_house_rent = monthly_rent exemptions = calculate_annual_eligible_hra_exemption(doc) if exemptions["monthly_exemption"]: # calc total exemption amount eligible_hra = exemptions["monthly_exemption"] * factor exemptions["monthly_house_rent"] = monthly_rent exemptions["total_eligible_hra_exemption"] = eligible_hra return exemptions
def get_leave_details(self, joining_date=None, relieving_date=None, lwp=None, for_preview=0): if not joining_date: joining_date, relieving_date = dataent.get_cached_value( "Employee", self.employee, ["date_of_joining", "relieving_date"]) working_days = date_diff(self.end_date, self.start_date) + 1 if for_preview: self.total_working_days = working_days self.payment_days = working_days return holidays = self.get_holidays_for_employee(self.start_date, self.end_date) actual_lwp = self.calculate_lwp(holidays, working_days) if not cint( dataent.db.get_value( "HR Settings", None, "include_holidays_in_total_working_days")): working_days -= len(holidays) if working_days < 0: dataent.throw( _("There are more holidays than working days this month.")) if not lwp: lwp = actual_lwp elif lwp != actual_lwp: dataent.msgprint( _("Leave Without Pay does not match with approved Leave Application records" )) self.total_working_days = working_days self.leave_without_pay = lwp payment_days = flt(self.get_payment_days(joining_date, relieving_date)) - flt(lwp) self.payment_days = payment_days > 0 and payment_days or 0
def on_submit(self): company = dataent.db.get_value("Employee", self.employee, "company") date_difference = date_diff(self.work_end_date, self.work_from_date) + 1 if self.half_day: date_difference -= 0.5 leave_period = get_leave_period(self.work_from_date, self.work_end_date, company) if leave_period: leave_allocation = self.exists_allocation_for_period(leave_period) if leave_allocation: leave_allocation.new_leaves_allocated += date_difference leave_allocation.submit() else: leave_allocation = self.create_leave_allocation( leave_period, date_difference) self.db_set("leave_allocation", leave_allocation.name) else: dataent.throw( _("There is no leave period in between {0} and {1}").format( self.work_from_date, self.work_end_date))
def validate_applicable_after(self): if self.leave_type: leave_type = dataent.get_doc("Leave Type", self.leave_type) if leave_type.applicable_after > 0: date_of_joining = dataent.db.get_value("Employee", self.employee, "date_of_joining") leave_days = get_approved_leaves_for_period( self.employee, False, date_of_joining, self.from_date) number_of_days = date_diff(getdate(self.from_date), date_of_joining) if number_of_days >= 0: holidays = 0 if not dataent.db.get_value("Leave Type", self.leave_type, "include_holiday"): holidays = get_holidays(self.employee, date_of_joining, self.from_date) number_of_days = number_of_days - leave_days - holidays if number_of_days < leave_type.applicable_after: dataent.throw( _("{0} applicable after {1} working days").format( self.leave_type, leave_type.applicable_after))
def get_depreciation_amount_prorata_temporis(self, depreciable_value, row, start_date=None, end_date=None): if start_date and end_date: prorata_temporis = min( abs(flt(date_diff(str(end_date), str(start_date)))) / flt( dataent.db.get_value("Asset Settings", None, "number_of_days_in_fiscal_year")), 1) else: prorata_temporis = 1 if row.depreciation_method in ("Straight Line", "Manual"): depreciation_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked)) * prorata_temporis else: depreciation_amount = self.get_depreciation_amount( depreciable_value, row.total_number_of_depreciations, row) return depreciation_amount