@@ -55,6 +55,35 @@ def setUp(self):
5555 )
5656 self .client .force_login (self .user )
5757
58+ def create_extended_service (
59+ self ,
60+ * ,
61+ enabled : bool = True ,
62+ expires = None ,
63+ maintenance_window : str = "" ,
64+ ) -> Service :
65+ Package .objects .get_or_create (name = "community" , defaults = {"price" : 0 })
66+ package , _ = Package .objects .get_or_create (
67+ name = "extended" ,
68+ defaults = {
69+ "verbose" : "Extended support" ,
70+ "price" : 600 ,
71+ "category" : PackageCategory .PACKAGE_SUPPORT ,
72+ },
73+ )
74+ customer = self .create_customer ()
75+ payment = Payment .objects .create (customer = customer , amount = 1 )
76+ service = Service .objects .create (
77+ customer = customer , maintenance_window = maintenance_window
78+ )
79+ service .subscription_set .create (
80+ package = package ,
81+ expires = expires or timezone .now () + timedelta (days = 30 ),
82+ enabled = enabled ,
83+ payment = payment .pk ,
84+ )
85+ return service
86+
5887 def test_customer_merge (self ):
5988 customer1 = self .create_customer ("TEST CUSTOMER 1" )
6089 customer2 = self .create_customer ("TEST CUSTOMER 2" )
@@ -189,6 +218,116 @@ def test_service(self):
189218 subscription1 .refresh_from_db ()
190219 self .assertFalse (subscription1 .enabled )
191220
221+ def test_service_maintenance_window (self ):
222+ service = self .create_extended_service ()
223+
224+ response = self .client .get (service .get_absolute_url ())
225+ self .assertContains (response , 'name="maintenance_window"' )
226+ self .assertContains (response , "Update maintenance window" )
227+
228+ response = self .client .post (
229+ service .get_absolute_url (),
230+ {
231+ "update_maintenance_window" : 1 ,
232+ "maintenance_window" : "Sundays 02:00 UTC" ,
233+ },
234+ )
235+ self .assertRedirects (response , service .get_absolute_url ())
236+
237+ service .refresh_from_db ()
238+ self .assertEqual (service .maintenance_window , "Sundays 02:00 UTC" )
239+
240+ interaction = Interaction .objects .get (customer = service .customer )
241+ self .assertEqual (interaction .origin , Interaction .Origin .MAINTENANCE_WINDOW )
242+ self .assertEqual (interaction .user , self .user )
243+ self .assertEqual (
244+ interaction .summary , f"Maintenance window updated for service { service .pk } "
245+ )
246+ self .assertEqual (interaction .content , "Maintenance window: Sundays 02:00 UTC" )
247+
248+ response = self .client .post (
249+ service .get_absolute_url (),
250+ {
251+ "update_maintenance_window" : 1 ,
252+ "maintenance_window" : "Sundays 02:00 UTC" ,
253+ },
254+ )
255+ self .assertRedirects (response , service .get_absolute_url ())
256+ self .assertEqual (
257+ Interaction .objects .filter (customer = service .customer ).count (), 1
258+ )
259+
260+ def test_service_maintenance_window_requires_active_extended_support (self ):
261+ Package .objects .create (name = "community" , price = 0 )
262+ basic = Package .objects .create (
263+ name = "basic" ,
264+ verbose = "Basic support" ,
265+ price = 300 ,
266+ category = PackageCategory .PACKAGE_SUPPORT ,
267+ )
268+ customer = self .create_customer ()
269+ payment = Payment .objects .create (customer = customer , amount = 1 )
270+ basic_service = Service .objects .create (customer = customer )
271+ basic_service .subscription_set .create (
272+ package = basic ,
273+ expires = timezone .now () + timedelta (days = 30 ),
274+ payment = payment .pk ,
275+ )
276+ expired_service = self .create_extended_service (
277+ expires = timezone .now () - timedelta (days = 1 )
278+ )
279+ disabled_service = self .create_extended_service (enabled = False )
280+
281+ for service in (basic_service , expired_service , disabled_service ):
282+ response = self .client .get (service .get_absolute_url ())
283+ self .assertNotContains (response , "Update maintenance window" )
284+
285+ response = self .client .post (
286+ service .get_absolute_url (),
287+ {
288+ "update_maintenance_window" : 1 ,
289+ "maintenance_window" : "Sundays 02:00 UTC" ,
290+ },
291+ )
292+ self .assertEqual (response .status_code , 403 )
293+ service .refresh_from_db ()
294+ self .assertEqual (service .maintenance_window , "" )
295+
296+ self .assertFalse (Interaction .objects .exists ())
297+
298+ def test_service_maintenance_window_requires_change_service_permission (self ):
299+ service = self .create_extended_service ()
300+ readonly_user = User .objects .create_user (
301+ username = "readonly" , email = "readonly@example.com" , is_staff = True
302+ )
303+ self .client .force_login (readonly_user )
304+
305+ response = self .client .get (service .get_absolute_url ())
306+ self .assertEqual (response .status_code , 403 )
307+
308+ response = self .client .post (
309+ service .get_absolute_url (),
310+ {
311+ "update_maintenance_window" : 1 ,
312+ "maintenance_window" : "Sundays 02:00 UTC" ,
313+ },
314+ )
315+ self .assertEqual (response .status_code , 403 )
316+
317+ service .refresh_from_db ()
318+ self .assertEqual (service .maintenance_window , "" )
319+ self .assertFalse (Interaction .objects .exists ())
320+
321+ def test_extended_service_list_shows_maintenance_window (self ):
322+ self .create_extended_service (maintenance_window = "Sundays 02:00 UTC" )
323+
324+ response = self .client .get (
325+ reverse ("crm:service-list" , kwargs = {"kind" : "extended" })
326+ )
327+
328+ self .assertContains (response , "Extended support services" )
329+ self .assertContains (response , "Sundays 02:00 UTC" )
330+
192331 @responses .activate
193332 def test_customer_quote (self ):
194333 cnb_mock_rates ()
0 commit comments