@@ -327,11 +327,133 @@ def test_preview(self):
327327 preview = cr .action_preview_changes ()
328328 self .assertEqual (preview ["_action" ], "create_group" )
329329 self .assertEqual (preview ["group_name" ], "Preview Group" )
330- self .assertEqual (preview ["new_member_count" ], 1 )
331- self .assertEqual (preview ["bank_count" ], 1 )
330+ # The new member is now a "_sections" detail block, not a count (OP#876).
331+ self .assertNotIn ("new_member_count" , preview )
332+ self .assertEqual (len (preview ["_sections" ]), 1 )
333+ # The bank line is now surfaced as a "_tables" entry, not a count (OP#876).
334+ self .assertNotIn ("bank_count" , preview )
335+ bank_tables = [t for t in preview ["_tables" ] if t ["title" ] == "Bank Accounts" ]
336+ self .assertEqual (len (bank_tables ), 1 )
337+ self .assertEqual (len (bank_tables [0 ]["rows" ]), 1 )
338+ self .assertIn ("12-34-56" , bank_tables [0 ]["rows" ][0 ])
332339 if self .head_kind :
333340 self .assertEqual (preview ["head_of_household" ], "PERSON, Head" )
334341
342+ def test_preview_one2many_as_tables_and_scalars (self ):
343+ """OP#876: phones / banks / ID docs are surfaced as `_tables` (actual
344+ data, not counts), and the previously-missing scalar fields are added."""
345+ id_type = self .env ["spp.vocabulary.code" ].search (
346+ [("vocabulary_id.namespace_uri" , "=" , "urn:openspp:vocab:id-type" )], limit = 1
347+ )
348+ cr = self ._make_cr (
349+ group_name = "Tables Group" ,
350+ address = "12 Rizal St" ,
351+ email = "group@example.com" ,
352+ phone_line_ids = [
353+ (0 , 0 , {"phone_no" : "+63911111111" , "is_primary" : True }),
354+ (0 , 0 , {"phone_no" : "+63922222222" }),
355+ ],
356+ bank_line_ids = [(0 , 0 , {"acc_number" : "ACC-1" , "acc_holder_name" : "Jane" })],
357+ id_doc_line_ids = ([(0 , 0 , {"id_type_id" : id_type .id , "value" : "ID-9" })] if id_type else []),
358+ )
359+ preview = cr .action_preview_changes ()
360+
361+ # Previously-missing scalar fields are now surfaced.
362+ self .assertEqual (preview ["email" ], "group@example.com" )
363+ self .assertEqual (preview ["address" ], "12 Rizal St" )
364+ self .assertIn ("area" , preview )
365+
366+ # Counts are replaced by the generic `_tables` contract.
367+ for removed in ("phone_count" , "bank_count" , "id_doc_count" ):
368+ self .assertNotIn (removed , preview )
369+
370+ titles = [t ["title" ] for t in preview ["_tables" ]]
371+ self .assertIn ("Phone Numbers" , titles )
372+ self .assertIn ("Bank Accounts" , titles )
373+ phones = next (t for t in preview ["_tables" ] if t ["title" ] == "Phone Numbers" )
374+ self .assertEqual (phones ["columns" ], ["Number" , "Country" , "Primary" ])
375+ self .assertEqual (len (phones ["rows" ]), 2 )
376+ self .assertIn ("+63911111111" , phones ["rows" ][0 ])
377+ if id_type :
378+ self .assertIn ("ID Documents" , titles )
379+
380+ def test_preview_members_table_and_sections (self ):
381+ """OP#876: existing members render as a Name/Role table; new members
382+ render as per-member detail sections (with their own phones)."""
383+ existing = self .partner_model .create ({"name" : "Existing Member A" , "is_registrant" : True , "is_group" : False })
384+ cr = self ._make_cr (
385+ group_name = "Members Group" ,
386+ member_existing_ids = [
387+ (
388+ 0 ,
389+ 0 ,
390+ {
391+ "individual_id" : existing .id ,
392+ "membership_type_id" : self .member_kind .id if self .member_kind else False ,
393+ },
394+ )
395+ ],
396+ member_new_ids = [
397+ (
398+ 0 ,
399+ 0 ,
400+ {
401+ "given_name" : "Nina" ,
402+ "family_name" : "Cruz" ,
403+ "birthdate" : "2000-05-05" ,
404+ "membership_type_id" : self .head_kind .id if self .head_kind else False ,
405+ "phone_line_ids" : [(0 , 0 , {"phone_no" : "+63999999999" , "is_primary" : True })],
406+ },
407+ )
408+ ],
409+ )
410+ preview = cr .action_preview_changes ()
411+
412+ # Counts are gone.
413+ self .assertNotIn ("existing_member_count" , preview )
414+ self .assertNotIn ("new_member_count" , preview )
415+
416+ # Existing members -> a Name/Role table.
417+ existing_tbl = [t for t in preview ["_tables" ] if t ["title" ] == "Existing Members" ]
418+ self .assertEqual (len (existing_tbl ), 1 )
419+ self .assertIn ("Existing Member A" , existing_tbl [0 ]["rows" ][0 ])
420+
421+ # New members -> a per-member detail section with fields + own phones.
422+ self .assertEqual (len (preview ["_sections" ]), 1 )
423+ sec = preview ["_sections" ][0 ]
424+ self .assertIn ("Nina" , sec ["title" ])
425+ labels = [f [0 ] for f in sec ["fields" ]]
426+ self .assertIn ("Date of Birth" , labels )
427+ self .assertIn ("Gender" , labels )
428+ self .assertTrue (any (t ["title" ] == "Phone Numbers" for t in sec ["tables" ]))
429+ self .assertIn ("+63999999999" , sec ["tables" ][0 ]["rows" ][0 ])
430+
431+ # The rendered review HTML surfaces the member data.
432+ html = cr ._generate_review_comparison_html ()
433+ self .assertIn ("Existing Members" , html )
434+ self .assertIn ("Existing Member A" , html )
435+ self .assertIn ("Nina" , html )
436+ self .assertIn ("+63999999999" , html )
437+
438+ def test_review_comparison_html_renders_tables (self ):
439+ """The review page HTML shows the actual phone / bank rows as tables,
440+ not a bare count (OP#876)."""
441+ cr = self ._make_cr (
442+ group_name = "HTML Group" ,
443+ email = "g@example.com" ,
444+ phone_line_ids = [(0 , 0 , {"phone_no" : "+63900000000" , "is_primary" : True })],
445+ bank_line_ids = [(0 , 0 , {"acc_number" : "BANK-777" })],
446+ )
447+ html = cr ._generate_review_comparison_html ()
448+ self .assertIn ("Phone Numbers" , html )
449+ self .assertIn ("+63900000000" , html )
450+ self .assertIn ("Bank Accounts" , html )
451+ self .assertIn ("BANK-777" , html )
452+ self .assertIn ("g@example.com" , html )
453+ # Raw count keys must not leak into the review output.
454+ self .assertNotIn ("phone_count" , html )
455+ self .assertNotIn ("bank_count" , html )
456+
335457 # ──────────────────────────────────────────────────────────────────
336458 # Wizard flow (OP#876 round 2): Add Member wizard, both modes
337459 # ──────────────────────────────────────────────────────────────────
0 commit comments