From 1a2e3e03b3617546d603f2772a900eb40ae39672 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sat, 4 Jul 2026 17:25:20 +0530 Subject: [PATCH 1/2] test: add coverage for Appointment Booking Settings slot validation --- .../test_appointment_booking_settings.py | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py index 6a78d53ba49..dd6b2cd5184 100644 --- a/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py +++ b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py @@ -1,9 +1,59 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2026, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -# import frappe + +import datetime + +import frappe from erpnext.tests.utils import ERPNextTestSuite class TestAppointmentBookingSettings(ERPNextTestSuite): - pass + """The settings validate each availability slot: from-time must precede to-time and + the slot length must be a whole multiple of the appointment duration.""" + + def make_settings(self, appointment_duration=30): + doc = frappe.new_doc("Appointment Booking Settings") + doc.appointment_duration = appointment_duration + return doc + + def dt(self, hms): + # the controller parses times against a fixed epoch date + return datetime.datetime.strptime("01/01/1970 " + hms, "%d/%m/%Y %H:%M:%S") + + def test_from_time_must_precede_to_time(self): + doc = self.make_settings() + record = frappe._dict(day_of_week="Monday") + self.assertRaises( + frappe.ValidationError, + doc.validate_from_and_to_time, + self.dt("18:00:00"), + self.dt("09:00:00"), + record, + ) + doc.validate_from_and_to_time(self.dt("09:00:00"), self.dt("18:00:00"), record) # valid order + + def test_slot_length_must_be_a_multiple_of_the_duration(self): + doc = self.make_settings(appointment_duration=30) + # 60 minutes is two 30-minute appointments -> fine + doc.duration_is_divisible(self.dt("09:00:00"), self.dt("10:00:00")) + # 45 minutes leaves a partial appointment -> rejected + self.assertRaises( + frappe.ValidationError, doc.duration_is_divisible, self.dt("09:00:00"), self.dt("09:45:00") + ) + + def test_validate_checks_every_slot(self): + doc = self.make_settings(appointment_duration=30) + doc.append( + "availability_of_slots", + {"day_of_week": "Monday", "from_time": "09:00:00", "to_time": "09:45:00"}, + ) + self.assertRaises(frappe.ValidationError, doc.validate) + + # a clean 60-minute slot passes end to end + doc.availability_of_slots = [] + doc.append( + "availability_of_slots", + {"day_of_week": "Monday", "from_time": "09:00:00", "to_time": "10:00:00"}, + ) + doc.validate() From c969ee7bef60dc42b303d979ab99a55dd6b7b2f0 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sat, 4 Jul 2026 18:44:04 +0530 Subject: [PATCH 2/2] test: use separate documents for the invalid and valid slot cases --- .../test_appointment_booking_settings.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py index dd6b2cd5184..f4cab812daa 100644 --- a/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py +++ b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py @@ -43,17 +43,17 @@ class TestAppointmentBookingSettings(ERPNextTestSuite): ) def test_validate_checks_every_slot(self): - doc = self.make_settings(appointment_duration=30) - doc.append( + bad = self.make_settings(appointment_duration=30) + bad.append( "availability_of_slots", {"day_of_week": "Monday", "from_time": "09:00:00", "to_time": "09:45:00"}, ) - self.assertRaises(frappe.ValidationError, doc.validate) + self.assertRaises(frappe.ValidationError, bad.validate) # a clean 60-minute slot passes end to end - doc.availability_of_slots = [] - doc.append( + good = self.make_settings(appointment_duration=30) + good.append( "availability_of_slots", {"day_of_week": "Monday", "from_time": "09:00:00", "to_time": "10:00:00"}, ) - doc.validate() + good.validate()