524 lines
21 KiB
Python
524 lines
21 KiB
Python
from datetime import datetime
|
|
from freezegun import freeze_time
|
|
from unittest.mock import patch
|
|
|
|
|
|
from odoo.addons.appointment_google_reserve.tests.common import GoogleReserveCommon
|
|
from odoo.tests import tagged
|
|
|
|
|
|
@tagged('appointment_google_reserve')
|
|
class AppointmentGoogleReserveAPITest(GoogleReserveCommon):
|
|
|
|
@freeze_time('2022-02-14 07-00-00')
|
|
def test_appointment_resource_sync(self):
|
|
# initial assertion
|
|
self.assertFalse(self.apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# 1. write on unrelated field: nothing happens
|
|
self.apt_resources_google.write({'name': 'Updated Name'})
|
|
self.assertFalse(self.apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# 2. write on capacity: update related appointment availabilities
|
|
self.apt_resources_google.write({'capacity': 3})
|
|
self.assertTrue(self.apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# reset sync
|
|
self.apt_type_resource_google.google_reserve_pending_sync = False
|
|
|
|
# 3. archive -> update related appointment availabilities
|
|
self.apt_resources_google.action_archive()
|
|
self.assertTrue(self.apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# reset sync
|
|
self.apt_type_resource_google.google_reserve_pending_sync = False
|
|
|
|
# 4. restore -> update related appointment availabilities (again)
|
|
self.apt_resources_google.action_unarchive()
|
|
self.assertTrue(self.apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# reset sync
|
|
self.apt_type_resource_google.google_reserve_pending_sync = False
|
|
|
|
# 4. restore -> update related appointment availabilities (again)
|
|
self.apt_resources_google.unlink()
|
|
self.assertTrue(self.apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
@freeze_time('2022-02-14 07-00-00')
|
|
@patch('odoo.addons.appointment_google_reserve.tools.google_reserve_iap.iap_jsonrpc')
|
|
def test_appointment_type_sync(self, iap_jsonrpc):
|
|
""" When an appointment is created, we need to check that it's registered on IAP.
|
|
When an appointment is modified, we need to check that:
|
|
- It's updated on IAP
|
|
- It's marked as requiring sync
|
|
|
|
The following use cases are taken into account:
|
|
- Creation of an appointment.type
|
|
- Modification of an appointment.type
|
|
- Only for relevant fields
|
|
- Some fields need to re-register the appointment on IAP
|
|
- Some fields need to update the availabilities on IAP
|
|
- (Some need to do both)
|
|
- Archive/Restore an appointment.type
|
|
- Unlink an appointment.type """
|
|
|
|
db_uuid = self.env['ir.config_parameter'].sudo().get_param('database.uuid')
|
|
self.env['ir.config_parameter'].sudo().set_param(
|
|
'appointment_google_reserve.google_reserve_iap_endpoint',
|
|
'https://google-reserve.api.odoo.com'
|
|
)
|
|
|
|
# 1. create an appointment.type: needs to be registered on IAP
|
|
apt_type_resource_google = self.env['appointment.type'].create({
|
|
'appointment_tz': 'UTC',
|
|
'is_auto_assign': True,
|
|
'is_date_first': True,
|
|
'location_id': self.test_location.id,
|
|
'min_schedule_hours': 1.0,
|
|
'max_schedule_days': 5,
|
|
'name': 'Test Google Reserve',
|
|
'manage_capacity': True,
|
|
'schedule_based_on': 'resources',
|
|
'google_reserve_enable': True,
|
|
'google_reserve_merchant_id': self.google_reserve_merchant.id,
|
|
'slot_ids': [(0, 0, {
|
|
'weekday': str(self.reference_monday.isoweekday()),
|
|
'start_hour': 15,
|
|
'end_hour': 18,
|
|
})],
|
|
})
|
|
access_token = apt_type_resource_google.google_reserve_access_token
|
|
|
|
# assert initial data
|
|
self.assertFalse(apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
self.env['appointment.resource'].create([{
|
|
'appointment_type_ids': apt_type_resource_google.ids,
|
|
'capacity': 2,
|
|
'name': 'Table 1',
|
|
'sequence': 2,
|
|
}, {
|
|
'appointment_type_ids': apt_type_resource_google.ids,
|
|
'capacity': 4,
|
|
'name': 'Table 2',
|
|
'sequence': 1,
|
|
}])
|
|
|
|
appointment_data = {
|
|
'db_uuid': db_uuid,
|
|
'merchant_details': {
|
|
'name': 'My Company',
|
|
'id': self.google_reserve_merchant.id,
|
|
'callback_url': apt_type_resource_google.get_base_url(),
|
|
'business_category': 'Restaurant',
|
|
'phone': '+32499123456',
|
|
'website_url': 'https://www.example.com',
|
|
'location': {
|
|
'country_code': 'BE',
|
|
'city': 'Bloups',
|
|
'region': False,
|
|
'zip': '6666',
|
|
'street': 'Zboing Street 42',
|
|
}
|
|
},
|
|
'appointment_details': {
|
|
'appointment_type_id': apt_type_resource_google.id,
|
|
'google_reserve_access_token': apt_type_resource_google.google_reserve_access_token,
|
|
'service_name': 'Test Google Reserve',
|
|
'min_cancellation_hours': 1.0,
|
|
'min_schedule_hours': 1.0
|
|
}
|
|
}
|
|
|
|
self.assertEqual(iap_jsonrpc.call_count, 1)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[0],
|
|
'https://google-reserve.api.odoo.com/api/google_reserve/1/appointment/register',
|
|
appointment_data,
|
|
)
|
|
|
|
# 2a. writing on an unrelated field, should not call IAP
|
|
apt_type_resource_google.write({'sequence': 42})
|
|
self.assertEqual(iap_jsonrpc.call_count, 1)
|
|
self.assertFalse(apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# 2b. writing on a field that alters both the slots and the appointment definition on IAP
|
|
# -> call register and mark as needing sync
|
|
apt_type_resource_google.write({'min_cancellation_hours': 2})
|
|
appointment_data['appointment_details']['min_cancellation_hours'] = 2
|
|
|
|
self.assertEqual(iap_jsonrpc.call_count, 2)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[1],
|
|
'https://google-reserve.api.odoo.com/api/google_reserve/1/appointment/register',
|
|
appointment_data,
|
|
)
|
|
self.assertTrue(apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# reset sync
|
|
apt_type_resource_google.google_reserve_pending_sync = False
|
|
|
|
# 2c. writing on a field that alters only the appointment definition
|
|
# -> call appointment registration route for update
|
|
apt_type_resource_google.write({'name': 'Test Google Reserve Edited'})
|
|
self.assertTrue(apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
self.assertEqual(iap_jsonrpc.call_count, 3)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[2],
|
|
'https://google-reserve.api.odoo.com/api/google_reserve/1/appointment/register',
|
|
False, # already tested before
|
|
)
|
|
|
|
# reset sync
|
|
apt_type_resource_google.google_reserve_pending_sync = False
|
|
|
|
# 2d. writing on a field that alters only the appointment availabilities
|
|
# -> call appointment availabilities replace route
|
|
apt_type_resource_google.write({'appointment_duration': 2})
|
|
self.assertTrue(apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# reset sync
|
|
apt_type_resource_google.google_reserve_pending_sync = False
|
|
|
|
# 3. archive -> unregister the appointment from IAP
|
|
apt_type_resource_google.action_archive()
|
|
self.assertEqual(iap_jsonrpc.call_count, 4)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[3],
|
|
f'https://google-reserve.api.odoo.com/api/google_reserve/1/appointment/{access_token}/unregister',
|
|
False, # nothing relevant to test
|
|
)
|
|
self.assertTrue(apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# reset sync
|
|
apt_type_resource_google.google_reserve_pending_sync = False
|
|
|
|
# 4. restore -> register the appointment on IAP again
|
|
apt_type_resource_google.action_unarchive()
|
|
self.assertEqual(iap_jsonrpc.call_count, 5)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[4],
|
|
'https://google-reserve.api.odoo.com/api/google_reserve/1/appointment/register',
|
|
False, # already tested before
|
|
)
|
|
self.assertTrue(apt_type_resource_google.google_reserve_pending_sync)
|
|
|
|
# reset sync
|
|
apt_type_resource_google.google_reserve_pending_sync = False
|
|
|
|
# 5. unlink -> unregister the appointment from IAP
|
|
apt_type_resource_google.unlink()
|
|
self.assertEqual(iap_jsonrpc.call_count, 6)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[5],
|
|
f'https://google-reserve.api.odoo.com/api/google_reserve/1/appointment/{access_token}/unregister',
|
|
False, # already tested before
|
|
)
|
|
|
|
@freeze_time('2022-02-14 07-00-00')
|
|
@patch('odoo.addons.appointment_google_reserve.tools.google_reserve_iap.iap_jsonrpc')
|
|
def test_booking_update(self, iap_jsonrpc):
|
|
""" When a calendar.event was created from a Google Reserve booking, we need to notify Google
|
|
every time that booking is updated (or cancelled)
|
|
|
|
The following use cases are taken into account:
|
|
- Update of a unrelated calendar.event
|
|
- Update of a Google Reserve calendar.event
|
|
- Date change
|
|
- Party size change
|
|
- Unrelated field change
|
|
- Archive a Google Reserve calendar.event
|
|
- Unlink a Google Reserve calendar.event """
|
|
|
|
update_availabilities_url = f'https://google-reserve.api.odoo.com/api/google_reserve/1/appointment/{self.apt_type_resource_google.google_reserve_access_token}/update_availabilities'
|
|
update_booking_url = f'https://google-reserve.api.odoo.com/api/google_reserve/1/appointment/{self.apt_type_resource_google.google_reserve_access_token}/update_booking'
|
|
|
|
calendar_event = self.env['calendar.event'].create({
|
|
'appointment_type_id': self.apt_type_resource_google.id,
|
|
'name': "New Meeting 1",
|
|
'total_capacity_reserved': 4,
|
|
'resource_ids': self.apt_type_resources_table_1.ids,
|
|
'start': datetime(2022, 2, 14, 10, 0, 0),
|
|
'stop': datetime(2022, 2, 14, 14, 0, 0),
|
|
})
|
|
|
|
calendar_event.write({
|
|
'stop': datetime(2022, 2, 14, 15, 0, 0)
|
|
})
|
|
|
|
# 1. not a Google Reserve event: no need to update Google
|
|
# we however did 2 calls to "update_availabilities"
|
|
self.assertEqual(iap_jsonrpc.call_count, 2)
|
|
for i in range(2):
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[i],
|
|
update_availabilities_url,
|
|
)
|
|
|
|
# 2. write on dates -> update Google to reflect calendar event changes
|
|
calendar_event.write({
|
|
'is_google_reserve': True,
|
|
'stop': datetime(2022, 2, 14, 14, 0, 0),
|
|
})
|
|
self.assertEqual(iap_jsonrpc.call_count, 4)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[2],
|
|
update_availabilities_url,
|
|
)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[3],
|
|
update_booking_url,
|
|
{
|
|
'booking_ids': calendar_event.ids,
|
|
'booking_values': {
|
|
'startTime': '2022-02-14T10:00:00Z',
|
|
'duration': str(4 * 3600) + 's' # 4 hours
|
|
},
|
|
'db_uuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid'),
|
|
}
|
|
)
|
|
|
|
# 3. book for more people -> update Google to reflect calendar event changes
|
|
calendar_event.write({
|
|
'resource_ids': [(4, self.apt_type_resources_table_2.id, 0)],
|
|
'total_capacity_reserved': 6,
|
|
})
|
|
self.assertEqual(iap_jsonrpc.call_count, 6)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[4],
|
|
update_availabilities_url,
|
|
)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[5],
|
|
update_booking_url,
|
|
{
|
|
'booking_ids': calendar_event.ids,
|
|
'booking_values': {
|
|
'partySize': '6',
|
|
},
|
|
'db_uuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid'),
|
|
}
|
|
)
|
|
|
|
# 4. name change -> nothing happens
|
|
calendar_event.write({
|
|
'name': 'Booking for Paul',
|
|
})
|
|
self.assertEqual(iap_jsonrpc.call_count, 6)
|
|
|
|
# 5. archive event -> cancel on Google
|
|
calendar_event.write({
|
|
'active': False,
|
|
})
|
|
self.assertEqual(iap_jsonrpc.call_count, 8)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[6],
|
|
update_availabilities_url,
|
|
)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[7],
|
|
update_booking_url,
|
|
{
|
|
'booking_ids': calendar_event.ids,
|
|
'booking_values': {
|
|
'status': 'CANCELED',
|
|
},
|
|
'db_uuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid'),
|
|
}
|
|
)
|
|
|
|
# 6. unlink event -> cancel on Google
|
|
[regular_event, google_event] = self.env['calendar.event'].create([{
|
|
'appointment_type_id': self.apt_type_resource_google.id,
|
|
'name': "New Meeting 2",
|
|
'resource_ids': self.apt_type_resources_table_1.ids,
|
|
'total_capacity_reserved': 4,
|
|
'start': datetime(2022, 2, 15, 10, 0, 0),
|
|
'stop': datetime(2022, 2, 15, 14, 0, 0),
|
|
}, {
|
|
'appointment_type_id': self.apt_type_resource_google.id,
|
|
'name': "New Meeting 3",
|
|
'resource_ids': self.apt_type_resources_table_1.ids,
|
|
'total_capacity_reserved': 4,
|
|
'start': datetime(2022, 2, 16, 10, 0, 0),
|
|
'stop': datetime(2022, 2, 16, 14, 0, 0),
|
|
'is_google_reserve': True,
|
|
}])
|
|
self.assertEqual(iap_jsonrpc.call_count, 9)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[8],
|
|
update_availabilities_url,
|
|
)
|
|
|
|
(regular_event + google_event).unlink()
|
|
self.assertEqual(iap_jsonrpc.call_count, 11)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[9],
|
|
update_availabilities_url,
|
|
)
|
|
self.assertIapRpc(
|
|
iap_jsonrpc.call_args_list[10],
|
|
update_booking_url,
|
|
{
|
|
'booking_ids': google_event.ids,
|
|
'booking_values': {
|
|
'status': 'CANCELED',
|
|
},
|
|
'db_uuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid'),
|
|
}
|
|
)
|
|
|
|
@freeze_time('2022-02-14 07-00-00')
|
|
@patch('odoo.addons.appointment_google_reserve.tools.google_reserve_iap.GoogleReserveIAP.update_availabilities', autospec=True)
|
|
def test_calendar_event_sync(self, update_availabilities):
|
|
""" When a calendar.event is created or modified, we need to sync the availabilities of the
|
|
underlying appointments that are google reserve enabled.
|
|
|
|
The following use cases are taken into account:
|
|
- Creation of a calendar.event
|
|
- Modification of a calendar.event (for relevant fields)
|
|
- Archive/Restore a calendar.event
|
|
- Unlink a calendar.event """
|
|
|
|
# 1. create bookings: update related appointment availabilities
|
|
events = self.env['calendar.event'].create([{
|
|
'appointment_type_id': self.apt_type_resource_google.id,
|
|
'name': "New Meeting 1",
|
|
'start': datetime(2022, 2, 14, 10, 0, 0),
|
|
'stop': datetime(2022, 2, 14, 15, 0, 0),
|
|
}, {
|
|
'appointment_type_id': self.apt_type_resource_google.id,
|
|
'name': "New Meeting 2",
|
|
'start': datetime(2022, 2, 14, 17, 0, 0),
|
|
'stop': datetime(2022, 2, 14, 19, 0, 0),
|
|
}])
|
|
|
|
self.assertEqual(update_availabilities.call_count, 1)
|
|
self.assertUpdateAvailabilities(
|
|
update_availabilities.call_args_list[0],
|
|
self.apt_type_resource_google,
|
|
datetime(2022, 2, 14, 10, 0, 0),
|
|
datetime(2022, 2, 14, 19, 0, 0)
|
|
)
|
|
|
|
# 2. rename bookings: do nothing, it doesn't affect availabilities
|
|
events.write({'name': "Renamed Meeting"})
|
|
self.assertEqual(update_availabilities.call_count, 1)
|
|
|
|
# 3. move bookings: update related appointment availabilities
|
|
# -> take min (old start, new start) and max (old stop, new stop)
|
|
events.write({
|
|
'start': datetime(2022, 2, 14, 11, 0, 0),
|
|
'stop': datetime(2022, 2, 14, 20, 0, 0)
|
|
})
|
|
self.assertEqual(update_availabilities.call_count, 2)
|
|
self.assertUpdateAvailabilities(
|
|
update_availabilities.call_args_list[1],
|
|
self.apt_type_resource_google,
|
|
datetime(2022, 2, 14, 10, 0, 0),
|
|
datetime(2022, 2, 14, 20, 0, 0)
|
|
)
|
|
|
|
# 4. archive bookings: update related appointment availabilities
|
|
events.action_archive()
|
|
self.assertEqual(update_availabilities.call_count, 3)
|
|
self.assertUpdateAvailabilities(
|
|
update_availabilities.call_args_list[2],
|
|
self.apt_type_resource_google,
|
|
datetime(2022, 2, 14, 11, 0, 0),
|
|
datetime(2022, 2, 14, 20, 0, 0)
|
|
)
|
|
|
|
# 5. restore bookings: update related appointment availabilities
|
|
events.action_unarchive()
|
|
self.assertEqual(update_availabilities.call_count, 4)
|
|
self.assertUpdateAvailabilities(
|
|
update_availabilities.call_args_list[3],
|
|
self.apt_type_resource_google,
|
|
datetime(2022, 2, 14, 11, 0, 0),
|
|
datetime(2022, 2, 14, 20, 0, 0)
|
|
)
|
|
|
|
# 6. delete bookings: update related appointment availabilities
|
|
events.unlink()
|
|
self.assertEqual(update_availabilities.call_count, 5)
|
|
self.assertUpdateAvailabilities(
|
|
update_availabilities.call_args_list[4],
|
|
self.apt_type_resource_google,
|
|
datetime(2022, 2, 14, 11, 0, 0),
|
|
datetime(2022, 2, 14, 20, 0, 0)
|
|
)
|
|
self.assertFalse(events.exists())
|
|
|
|
@freeze_time('2022-02-14 07-00-00')
|
|
@patch('odoo.addons.appointment_google_reserve.tools.google_reserve_iap.GoogleReserveIAP.update_availabilities', autospec=True)
|
|
def test_resource_calendar_leaves_sync(self, update_availabilities):
|
|
""" When a resource.calendar.leaves is created or modified, we need to sync the
|
|
availabilities of the underlying appointment resources' appointments that are google reserve enabled.
|
|
|
|
The following use cases are taken into account:
|
|
- Creation of a calendar.resource.leaves
|
|
- Modification of a calendar.resource.leaves (for relevant fields)
|
|
- Unlink a calendar.resource.leaves """
|
|
|
|
# 1. create leaves: update related appointment availabilities
|
|
calendar_leaves = self.env['resource.calendar.leaves'].create([{
|
|
'resource_id': appointment_resource.resource_id.id,
|
|
'date_from': datetime(2022, 2, 17, 8, 0, 0),
|
|
'date_to': datetime(2022, 2, 17, 17, 0, 0),
|
|
} for appointment_resource in self.apt_resources_google])
|
|
|
|
self.assertEqual(update_availabilities.call_count, 1)
|
|
self.assertUpdateAvailabilities(
|
|
update_availabilities.call_args_list[0],
|
|
self.apt_type_resource_google,
|
|
datetime(2022, 2, 17, 8, 0, 0),
|
|
datetime(2022, 2, 17, 17, 0, 0)
|
|
)
|
|
|
|
# 2. change reason: do nothing, it doesn't affect availabilities
|
|
calendar_leaves.write({'name': "New Reason"})
|
|
self.assertEqual(update_availabilities.call_count, 1)
|
|
|
|
# 3. move leaves: update related appointment availabilities
|
|
# -> take min (old start, new start) and max (old stop, new stop)
|
|
calendar_leaves.write({
|
|
'date_from': datetime(2022, 2, 17, 11, 0, 0),
|
|
'date_to': datetime(2022, 2, 17, 20, 0, 0)
|
|
})
|
|
self.assertEqual(update_availabilities.call_count, 2)
|
|
self.assertUpdateAvailabilities(
|
|
update_availabilities.call_args_list[1],
|
|
self.apt_type_resource_google,
|
|
datetime(2022, 2, 17, 8, 0, 0),
|
|
datetime(2022, 2, 17, 20, 0, 0)
|
|
)
|
|
|
|
# 4. unlink: update related appointment availabilities
|
|
calendar_leaves.unlink()
|
|
self.assertUpdateAvailabilities(
|
|
update_availabilities.call_args_list[2],
|
|
self.apt_type_resource_google,
|
|
datetime(2022, 2, 17, 11, 0, 0),
|
|
datetime(2022, 2, 17, 20, 0, 0)
|
|
)
|
|
|
|
# ------------------------------------------------------------
|
|
# UTILS
|
|
# ------------------------------------------------------------
|
|
|
|
def assertIapRpc(self, post_mock_call_args, post_url, post_data=False):
|
|
args, kwargs = post_mock_call_args
|
|
self.assertEqual(args[0], post_url)
|
|
if post_data:
|
|
self.assertDictEqual(kwargs.get('params'), post_data)
|
|
|
|
def assertUpdateAvailabilities(self, update_availabilities_call_args, appointment, start=None, stop=None):
|
|
args, _kwargs = update_availabilities_call_args
|
|
self.assertEqual(args[1], appointment)
|
|
if start is not None:
|
|
self.assertEqual(args[2], start)
|
|
if stop is not None:
|
|
self.assertEqual(args[3], stop)
|