Merge branch 'develop' of https://github.com/frappe/erpnext into develop
diff --git a/erpnext/crm/doctype/appointment/__init__.py b/erpnext/crm/doctype/appointment/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/__init__.py
diff --git a/erpnext/crm/doctype/appointment/appointment.js b/erpnext/crm/doctype/appointment/appointment.js
new file mode 100644
index 0000000..4e41047
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/appointment.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Appointment', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/crm/doctype/appointment/appointment.json b/erpnext/crm/doctype/appointment/appointment.json
new file mode 100644
index 0000000..24cbd92
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/appointment.json
@@ -0,0 +1,91 @@
+{
+ "autoname": "format:APMT-{appointment_date}-{####}",
+ "creation": "2019-08-27 10:48:27.926283",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "from_time",
+ "to_time",
+ "appointment_date",
+ "customer_details_section",
+ "customer_name",
+ "customer_phone_number",
+ "customer_skype",
+ "customer_details"
+ ],
+ "fields": [
+ {
+ "fieldname": "from_time",
+ "fieldtype": "Time",
+ "in_list_view": 1,
+ "label": "From Time",
+ "reqd": 1
+ },
+ {
+ "fieldname": "to_time",
+ "fieldtype": "Time",
+ "in_list_view": 1,
+ "label": "To Tme",
+ "reqd": 1
+ },
+ {
+ "fieldname": "appointment_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Date ",
+ "reqd": 1
+ },
+ {
+ "fieldname": "customer_details_section",
+ "fieldtype": "Section Break",
+ "label": "Customer Details"
+ },
+ {
+ "fieldname": "customer_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Name",
+ "reqd": 1
+ },
+ {
+ "fieldname": "customer_phone_number",
+ "fieldtype": "Data",
+ "label": "Phone Number"
+ },
+ {
+ "fieldname": "customer_skype",
+ "fieldtype": "Data",
+ "label": "Skype ID"
+ },
+ {
+ "fieldname": "customer_details",
+ "fieldtype": "Long Text",
+ "label": "Details"
+ }
+ ],
+ "modified": "2019-08-27 12:43:30.143937",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Appointment",
+ "name_case": "UPPER CASE",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py
new file mode 100644
index 0000000..204b066
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/appointment.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class Appointment(Document):
+ pass
diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py
new file mode 100644
index 0000000..702ac71
--- /dev/null
+++ b/erpnext/crm/doctype/appointment/test_appointment.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestAppointment(unittest.TestCase):
+ pass
diff --git a/erpnext/crm/doctype/appointment_booking_settings/__init__.py b/erpnext/crm/doctype/appointment_booking_settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/__init__.py
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
new file mode 100644
index 0000000..465df2c
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
@@ -0,0 +1,18 @@
+// frappe.ui.form.on('Availability Of Slots', 'from_time', check_time)
+// frappe.ui.form.on('Availability Of Slots', 'to_time', check_time)
+
+frappe.ui.form.on('Appointment Booking Settings', 'validate',check_times)
+function check_times(frm) {
+ $.each(frm.doc.availability_of_slots || [], function (i, d) {
+ let from_time = Date.parse('01/01/2019 ' + d.from_time);
+ console.log(from_time);
+ let to_time = Date.parse('01/01/2019 ' + d.to_time);
+ if (from_time > to_time) {
+ frappe.throw(__(`In row ${i + 1} of Availability Of Slots : "To Time" must be later than "From Time"`))
+ }
+ })
+}
+// function check_times(frm, cdt, cdn) {
+ // let d = locals[cdt][cdn];
+//
+// }
\ No newline at end of file
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
new file mode 100644
index 0000000..ed6150a
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json
@@ -0,0 +1,72 @@
+{
+ "creation": "2019-08-27 10:56:48.309824",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "availability_of_slots",
+ "number_of_agents",
+ "holiday_list",
+ "email_reminders",
+ "appointment_duration"
+ ],
+ "fields": [
+ {
+ "fieldname": "availability_of_slots",
+ "fieldtype": "Table",
+ "label": "Availability Of Slots",
+ "options": "Availability Of Slots",
+ "reqd": 1
+ },
+ {
+ "fieldname": "number_of_agents",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "No. Of Agents",
+ "reqd": 1
+ },
+ {
+ "fieldname": "holiday_list",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Holiday List",
+ "options": "Holiday List",
+ "reqd": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "email_reminders",
+ "fieldtype": "Check",
+ "label": "Email Reminders"
+ },
+ {
+ "default": "60",
+ "fieldname": "appointment_duration",
+ "fieldtype": "Int",
+ "label": "Appointment Duration",
+ "reqd": 1
+ }
+ ],
+ "issingle": 1,
+ "modified": "2019-08-27 17:32:46.208951",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Appointment Booking Settings",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
new file mode 100644
index 0000000..3307636
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class AppointmentBookingSettings(Document):
+ pass
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
new file mode 100644
index 0000000..3dc3c39
--- /dev/null
+++ b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestAppointmentBookingSettings(unittest.TestCase):
+ pass
diff --git a/erpnext/crm/doctype/availability_of_slots/__init__.py b/erpnext/crm/doctype/availability_of_slots/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/availability_of_slots/__init__.py
diff --git a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json
new file mode 100644
index 0000000..d26f7ce
--- /dev/null
+++ b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json
@@ -0,0 +1,46 @@
+{
+ "creation": "2019-08-27 10:52:54.204677",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "day_of_week",
+ "from_time",
+ "to_time"
+ ],
+ "fields": [
+ {
+ "fieldname": "day_of_week",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Day Of Week",
+ "options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday",
+ "reqd": 1
+ },
+ {
+ "fieldname": "from_time",
+ "fieldtype": "Time",
+ "in_list_view": 1,
+ "label": "From Time ",
+ "reqd": 1
+ },
+ {
+ "fieldname": "to_time",
+ "fieldtype": "Time",
+ "in_list_view": 1,
+ "label": "To Time",
+ "reqd": 1
+ }
+ ],
+ "istable": 1,
+ "modified": "2019-08-27 10:52:54.204677",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Availabilty Of Slots",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
new file mode 100644
index 0000000..8258471
--- /dev/null
+++ b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class AvailabilityOfSlots(Document):
+ pass
diff --git a/erpnext/crm/doctype/timezone/__init__.py b/erpnext/crm/doctype/timezone/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/crm/doctype/timezone/__init__.py
diff --git a/erpnext/crm/doctype/timezone/test_timezone.py b/erpnext/crm/doctype/timezone/test_timezone.py
new file mode 100644
index 0000000..92a8889
--- /dev/null
+++ b/erpnext/crm/doctype/timezone/test_timezone.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestTimezone(unittest.TestCase):
+ pass
diff --git a/erpnext/crm/doctype/timezone/timezone.js b/erpnext/crm/doctype/timezone/timezone.js
new file mode 100644
index 0000000..4dc57db
--- /dev/null
+++ b/erpnext/crm/doctype/timezone/timezone.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Timezone', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/crm/doctype/timezone/timezone.json b/erpnext/crm/doctype/timezone/timezone.json
new file mode 100644
index 0000000..9eb8ed9
--- /dev/null
+++ b/erpnext/crm/doctype/timezone/timezone.json
@@ -0,0 +1,52 @@
+{
+ "autoname": "field:timezone_name",
+ "creation": "2019-08-27 11:39:30.328670",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "offset",
+ "timezone_name"
+ ],
+ "fields": [
+ {
+ "fieldname": "offset",
+ "fieldtype": "Int",
+ "in_list_view": 1,
+ "label": "Offset In Minutes",
+ "reqd": 1
+ },
+ {
+ "fieldname": "timezone_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Name",
+ "reqd": 1,
+ "unique": 1
+ }
+ ],
+ "modified": "2019-08-27 11:39:30.328670",
+ "modified_by": "Administrator",
+ "module": "CRM",
+ "name": "Timezone",
+ "name_case": "Title Case",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/crm/doctype/timezone/timezone.py b/erpnext/crm/doctype/timezone/timezone.py
new file mode 100644
index 0000000..20e7d37
--- /dev/null
+++ b/erpnext/crm/doctype/timezone/timezone.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+# import frappe
+from frappe.model.document import Document
+
+class Timezone(Document):
+ pass
diff --git a/erpnext/www/book-appointment/1.html b/erpnext/www/book-appointment/1.html
new file mode 100644
index 0000000..db4ef26
--- /dev/null
+++ b/erpnext/www/book-appointment/1.html
@@ -0,0 +1,31 @@
+{% extends "templates/web.html" %}
+
+{% block title %}{{ _("Book Appointment") }}{% endblock %}
+
+{% block page_content %}
+<div class="container">
+ <!-- title: Book an appointment -->
+ <div class="text-center mb-5">
+ <h3>Book an appointment</h3>
+ <h4>Select the date and your timezone</h4>
+ </div>
+ <div class="row justify-content-center mt-3">
+ <div class="col-md-4 align-self-center ">
+ <form name="myform">
+ <input type="date" name="appointment-date" id="appointment-date" class="form-control mt-3" min="{{ from_date }}" max="{{ to_date }}">
+ <select name="appointment-timezone" id="appointment-timezone" class="form-control mt-3">
+ {% if timezones %}
+ {% for timezone in timezones%}
+ <option value="{{timezone.offset}}">{{timezone.timezone_name}}</option>
+ {% endfor %}
+ {% endif %}
+ </select>
+ </form>
+ <button class="form-control mt-3 btn btn-dark" id="next-button" onclick="next()">
+ Next
+ </button>
+ </div>
+ </div>
+</div>
+
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/www/book-appointment/1.js b/erpnext/www/book-appointment/1.js
new file mode 100644
index 0000000..d05c253
--- /dev/null
+++ b/erpnext/www/book-appointment/1.js
@@ -0,0 +1,14 @@
+
+let holidays = [];
+{% if holidays %}
+ holidays = {{holidays}}
+{% endif %}
+
+function next() {
+ let date = document.getElementsByName('appointment-date')[0].value;
+ if(holidays.includes(date)){
+ frappe.throw("That day is a holiday")
+ }
+ let tz = document.getElementsByName('appointment-timezone')[0].value;
+ window.location = `/book-appointment/2?date=${date}&tz=${tz}`;
+}
\ No newline at end of file
diff --git a/erpnext/www/book-appointment/1.py b/erpnext/www/book-appointment/1.py
new file mode 100644
index 0000000..95169b9
--- /dev/null
+++ b/erpnext/www/book-appointment/1.py
@@ -0,0 +1,17 @@
+import frappe
+
+def get_context(context):
+ settings = frappe.get_doc('Appointment Booking Settings')
+ holiday_list = frappe.get_doc('Holiday List',settings.holiday_list)
+ holidays = []
+ for holiday in holiday_list.holidays:
+ print(str(holiday.holiday_date))
+ holidays.append(str(holiday.holiday_date))
+ context.holidays = holidays
+ context.from_date = holiday_list.from_date
+ context.to_date = holiday_list.to_date
+ timezones = frappe.get_all('Timezone',fields=["timezone_name","offset"])
+ context.timezones = timezones
+
+ return context
+
diff --git a/erpnext/www/book-appointment/2.html b/erpnext/www/book-appointment/2.html
new file mode 100644
index 0000000..2a8c5c9
--- /dev/null
+++ b/erpnext/www/book-appointment/2.html
@@ -0,0 +1,60 @@
+{% extends "templates/web.html" %}
+
+{% block title %}{{ _("Book Appointment") }}{% endblock %}
+
+{% block page_content %}
+<style>
+ .time-slot {
+ margin: 0 0;
+ border: 0.5px solid #cccccc;
+ min-height: 100px;
+ }
+
+ .time-slot:hover {
+ background: #ddd;
+ }
+
+ .time-slot.unavailable {
+ background: #bbb;
+
+ color: #777777
+ }
+
+ input[type="radio"] {
+ visibility: hidden;
+ display: none;
+ }
+
+ .time-slot.selected {
+ color: white;
+ background: #5e64ff;
+ }
+</style>
+<div class="container">
+ <div class="text-center mb-5">
+ {% if is_holiday %}
+ <h3> This day is a holiday</h3>
+ {% else %}
+ <h3>Pick A Time Slot</h3>
+ <h4>Selected date is {{ date }}</h4>
+ </div>
+ <!-- Start of main content-->
+
+ <div class="mt-3 justify-content-center">
+ <div class="row">
+ {% for timeslot in timeslots %}
+ <div class="col-md time-slot {% if timeslot.unavailable %}unavailable{% endif %}" id="{{ timeslot.time.time() }}">{{ timeslot.time.time().strftime('%H : %M') }}</div>
+ {% endfor %}
+ </div>
+ <div class="row justify-content-center">
+ <div class="col-md-4 align-self-center">
+ <button class="form-control mt-5 btn btn-dark" onclick="next()">
+ Next
+ </button>
+ </div>
+ </div>
+ {% endif %}
+ </div>
+</div>
+<!-- End of main content -->
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/www/book-appointment/2.js b/erpnext/www/book-appointment/2.js
new file mode 100644
index 0000000..113564a
--- /dev/null
+++ b/erpnext/www/book-appointment/2.js
@@ -0,0 +1,31 @@
+let time_slot_divs = document.getElementsByClassName('time-slot');
+
+function get_available_slots() {
+ frappe.db
+}
+
+function select_time() {
+ if (this.classList.contains("unavailable")) {
+ return
+ }
+ console.log(this.id)
+ try{
+ selected_element = document.getElementsByClassName('selected')[0]
+ }catch(e){
+ this.classList.add('selected')
+ }
+ selected_element.classList.remove('selected');
+ this.classList.add('selected');
+}
+
+for (var i = 0; i < time_slot_divs.length; i++) {
+ time_slot_divs[i].addEventListener('click', select_time);
+}
+
+function next() {
+ let urlParams = new URLSearchParams(window.location.search);
+ let date = urlParams.get("date");
+ let tz = urlParams.get("tz");
+ let time_slot = document.querySelector(".selected").id;
+ window.location.href = `/book-appointment/3?date=${date}&tz=${tz}&time=${time_slot}`;
+}
\ No newline at end of file
diff --git a/erpnext/www/book-appointment/2.py b/erpnext/www/book-appointment/2.py
new file mode 100644
index 0000000..fa8aafa
--- /dev/null
+++ b/erpnext/www/book-appointment/2.py
@@ -0,0 +1,93 @@
+import frappe
+import datetime
+
+
+def get_context(context):
+ # Get query parameters
+ date = frappe.form_dict['date']
+ tz = frappe.form_dict['tz']
+ tz = int(tz)
+ # Database queries
+ settings = frappe.get_doc('Appointment Booking Settings')
+ holiday_list = frappe.get_doc('Holiday List', settings.holiday_list)
+ # Format datetimes
+ format_string = '%Y-%m-%d %H:%M:%S'
+ start_time = datetime.datetime.strptime(date+' 00:00:00', format_string)
+ end_time = datetime.datetime.strptime(date+' 23:59:59', format_string)
+ # Convert to ist
+ start_time = _convert_to_ist(start_time, tz)
+ end_time = _convert_to_ist(end_time, tz)
+ timeslots = get_available_slots_between(start_time, end_time, settings)
+ converted_timeslots = []
+ print('Appointments')
+ print(frappe.get_list('Appointment',fields=['from_time']))
+ for timeslot in timeslots:
+ if timeslot > end_time or timeslot < start_time:
+ pass
+ else:
+ if frappe.db.count('Appointment',{'from_time':start_time.time()}) < settings.number_of_agents:
+ converted_timeslots.append(dict(time=_convert_to_tz(timeslot, tz), unavailable=False))
+ else:
+ converted_timeslots.append(dict(time=_convert_to_tz(timeslot, tz),unavailable=True))
+
+ context.timeslots = converted_timeslots
+ context.date = date
+ return context
+
+def _is_holiday(date, holiday_list):
+ for holiday in holiday_list.holidays:
+ if holiday.holiday_date.isoformat() == date:
+ return True
+ return False
+
+def _convert_to_ist(datetime_object, timezone):
+ offset = datetime.timedelta(minutes=timezone)
+ datetime_object = datetime_object + offset
+ offset = datetime.timedelta(minutes=-330)
+ datetime_object = datetime_object - offset
+ return datetime_object
+
+def _convert_to_tz(datetime_object, timezone):
+ offset = datetime.timedelta(minutes=timezone)
+ datetime_object = datetime_object - offset
+ offset = datetime.timedelta(minutes=-330)
+ datetime_object = datetime_object + offset
+ return datetime_object
+
+def get_available_slots_between(start_time_parameter, end_time_parameter, settings):
+ records = get_records(start_time_parameter, end_time_parameter, settings)
+ timeslots = []
+ appointment_duration = datetime.timedelta(
+ minutes=settings.appointment_duration)
+ for record in records:
+ if record.day_of_week == weekdays[start_time_parameter.weekday()]:
+ current_time = _deltatime_to_datetime(
+ start_time_parameter, record.from_time)
+ end_time = _deltatime_to_datetime(
+ start_time_parameter, record.to_time)
+ elif record.day_of_week == weekdays[end_time_parameter.weekday()]:
+ current_time = _deltatime_to_datetime(
+ end_time_parameter, record.from_time)
+ end_time = _deltatime_to_datetime(
+ end_time_parameter, record.to_time)
+ while current_time + appointment_duration <= end_time:
+ timeslots.append(current_time)
+ current_time += appointment_duration
+ return timeslots
+
+
+def get_records(start_time, end_time, settings):
+ records = []
+ for record in settings.availability_of_slots:
+ if record.day_of_week == weekdays[start_time.weekday()] or record.day_of_week == weekdays[end_time.weekday()]:
+ records.append(record)
+ return records
+
+
+def _deltatime_to_datetime(date, deltatime):
+ time = (datetime.datetime.min + deltatime).time()
+ return datetime.datetime.combine(date.date(), time)
+
+
+weekdays = ["Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday", "Sunday"]
diff --git a/erpnext/www/book-appointment/3.html b/erpnext/www/book-appointment/3.html
new file mode 100644
index 0000000..b627a0c
--- /dev/null
+++ b/erpnext/www/book-appointment/3.html
@@ -0,0 +1,22 @@
+{% extends "templates/web.html" %}
+
+{% block title %}{{ _("Book Appointment") }}{% endblock %}
+
+{% block page_content %}
+<div class="container">
+
+ <div class="text-center mb-5">
+ <h3>Add details</h3>
+ <h4>Selected date is {{ date }} at {{ time }}</h4>
+ </div>
+ <div class="row justify-content-center mt-3">
+ <div class="col-md-4 align-items-center">
+ <input class="form-control mt-3" type="text" name="customer_name" id="customer_name" placeholder="Your Name" required>
+ <input class="form-control mt-3" type="tel" name="customer_number" id="customer_number" placeholder="Contact Number" required>
+ <input class="form-control mt-3" type="text" name="customer_skype" id="customer_skype" placeholder="Skype" required>
+ <textarea class="form-control mt-3" name="customer_notes" id="customer_notes" cols="30" rows="10" placeholder="Notes"></textarea>
+ <button class="btn btn-primary form-control mt-3" onclick="submit()">Submit</button>
+ </div>
+ </div>
+</div>
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/www/book-appointment/3.js b/erpnext/www/book-appointment/3.js
new file mode 100644
index 0000000..23c55a3
--- /dev/null
+++ b/erpnext/www/book-appointment/3.js
@@ -0,0 +1,11 @@
+function submit(){
+ let params = new URLSearchParams(window.location.search);
+ const date = params.get('date');
+ const time = params.get('time');
+ const tz = params.get('tz');
+ const customer_name = document.getElementById('customer_name').value;
+ const customer_number = document.getElementById('customer_number').value;
+ const customer_skype = document.getElementById('customer_skype').value;
+ const customer_notes = document.getElementById('customer_notes').value;
+ console.log({date,time,tz,customer_name,customer_number,customer_skype,customer_notes});
+}
\ No newline at end of file