@@ -221,6 +221,117 @@ def test_transient_vies_interactions_are_deleted(self) -> None:
221221 )
222222
223223
224+ class CRMInvoiceSearchTestCase (BaseCRMTestCase ):
225+ user : User
226+
227+ def setUp (self ):
228+ self .user = User .objects .create_superuser (
229+ username = "admin" , email = "admin@example.com"
230+ )
231+ self .client .force_login (self .user )
232+
233+ def create_search_invoice (
234+ self ,
235+ * ,
236+ customer_name : str ,
237+ description : str ,
238+ customer_email : str = "" ,
239+ kind : InvoiceKind = InvoiceKind .INVOICE ,
240+ ) -> Invoice :
241+ customer = self .create_customer (customer_name )
242+ if customer_email :
243+ customer .email = customer_email
244+ customer .save (update_fields = ["email" ])
245+ invoice = Invoice .objects .create (
246+ kind = kind ,
247+ category = InvoiceCategory .HOSTING ,
248+ customer = customer ,
249+ currency = Currency .EUR ,
250+ )
251+ invoice .invoiceitem_set .create (
252+ description = description , quantity = 1 , unit_price = Decimal (100 )
253+ )
254+ invoice .refresh_from_db ()
255+ return invoice
256+
257+ def test_invoice_search (self ):
258+ invoice = self .create_search_invoice (
259+ customer_name = "Acme Search Customer" ,
260+ customer_email = "billing-search@example.com" ,
261+ description = "Hosted invoice search item" ,
262+ )
263+ other_invoice = self .create_search_invoice (
264+ customer_name = "Other Customer" ,
265+ customer_email = "other@example.com" ,
266+ description = "Consulting invoice item" ,
267+ )
268+
269+ list_url = reverse ("crm:invoice-list" , kwargs = {"kind" : "all" })
270+ response = self .client .get (list_url )
271+ self .assertContains (response , invoice .number )
272+ self .assertContains (response , other_invoice .number )
273+ self .assertContains (response , 'type="search"' )
274+
275+ searches = (
276+ invoice .number ,
277+ "acme search" ,
278+ "billing-search@example.com" ,
279+ "hosted invoice search" ,
280+ )
281+ for query in searches :
282+ with self .subTest (query = query ):
283+ response = self .client .get (list_url , {"q" : query })
284+
285+ self .assertEqual (response .context ["query" ], query )
286+ self .assertContains (response , invoice .number )
287+ self .assertNotContains (response , other_invoice .number )
288+
289+ def test_invoice_search_deduplicates_line_item_matches (self ):
290+ invoice = self .create_search_invoice (
291+ customer_name = "Repeated Item Customer" ,
292+ description = "Repeated support item" ,
293+ )
294+ invoice .invoiceitem_set .create (
295+ description = "Repeated support item follow-up" ,
296+ quantity = 1 ,
297+ unit_price = Decimal (50 ),
298+ )
299+
300+ response = self .client .get (
301+ reverse ("crm:invoice-list" , kwargs = {"kind" : "all" }),
302+ {"q" : "Repeated support item" },
303+ )
304+
305+ self .assertContains (response , invoice .number , count = 1 )
306+
307+ def test_invoice_search_keeps_kind_filter (self ):
308+ invoice = self .create_search_invoice (
309+ customer_name = "Shared Search Customer" ,
310+ description = "Shared search invoice" ,
311+ )
312+ quote = self .create_search_invoice (
313+ customer_name = "Shared Search Customer" ,
314+ description = "Shared search quote" ,
315+ kind = InvoiceKind .QUOTE ,
316+ )
317+
318+ response = self .client .get (
319+ reverse ("crm:invoice-list" , kwargs = {"kind" : "quote" }),
320+ {"q" : "Shared Search Customer" },
321+ )
322+
323+ self .assertContains (response , quote .number )
324+ self .assertNotContains (response , invoice .number )
325+
326+ response = self .client .get (
327+ reverse ("crm:invoice-list" , kwargs = {"kind" : "invoice" }),
328+ {"q" : "Shared Search Customer" },
329+ )
330+
331+ self .assertContains (response , invoice .number )
332+ self .assertNotContains (response , quote .number )
333+
334+
224335class CRMTestCase (BaseCRMTestCase ):
225336 user : User
226337
0 commit comments