@@ -75,6 +75,19 @@ odoo.define("l10n_es_ticketbai_pos.models", function (require) {
7575
7676 return order . tbai_current_invoice
7777 . then ( ( tbai_inv ) => {
78+ // Check if export failed
79+ const exported = order . export_as_JSON ( ) ;
80+ if ( exported . tbai_export_failed ) {
81+ Gui . showPopup ( "Error" , {
82+ title : _t ( "TicketBAI" ) ,
83+ body : _t (
84+ "The TicketBAI invoice could not be signed. " +
85+ "Please refresh and try again."
86+ ) ,
87+ } ) ;
88+ return Promise . reject ( new Error ( "TicketBAI export failed" ) ) ;
89+ }
90+
7891 if ( tbai_inv ) {
7992 const tbai_last_invoice_data = {
8093 order : {
@@ -88,15 +101,15 @@ odoo.define("l10n_es_ticketbai_pos.models", function (require) {
88101 } ;
89102 this . set_tbai_last_invoice_data ( tbai_last_invoice_data ) ;
90103 }
91- // Even if null → still push the order (it's already validated)
92104 return super . push_single_order ( ...arguments ) ;
93105 } )
94106 . catch ( ( err ) => {
95- console . error (
96- "push_single_order: TicketBAI processing failed" ,
97- err
98- ) ;
99- // Do NOT block order sync — but log it
107+ console . error ( "push_single_order: TicketBAI failed" , err ) ;
108+ // ← BLOCK the push on TicketBAI errors
109+ if ( err . message === "TicketBAI export failed" ) {
110+ // Don't push
111+ return Promise . reject ( err ) ;
112+ }
100113 return super . push_single_order ( ...arguments ) ;
101114 } ) ;
102115 }
@@ -179,6 +192,8 @@ odoo.define("l10n_es_ticketbai_pos.models", function (require) {
179192 super ( ...arguments ) ;
180193 this . tbai_simplified_invoice = null ;
181194 this . tbai_current_invoice = $ . when ( ) ;
195+ // NEW: Prevent concurrent builds
196+ this . _tbai_build_lock = false ;
182197 if ( this . pos . company . tbai_enabled && "json" in arguments [ 1 ] ) {
183198 this . tbai_simplified_invoice =
184199 new tbai_models . TicketBAISimplifiedInvoice (
@@ -265,6 +280,7 @@ odoo.define("l10n_es_ticketbai_pos.models", function (require) {
265280 if ( tbai_inv !== null ) {
266281 const datas = tbai_inv . datas ;
267282 const signature_value = tbai_inv . signature_value ;
283+
268284 if ( datas !== null && signature_value !== null ) {
269285 json . tbai_signature_value = signature_value ;
270286 json . tbai_datas = datas ;
@@ -275,7 +291,18 @@ odoo.define("l10n_es_ticketbai_pos.models", function (require) {
275291 json . tbai_previous_order_pos_reference =
276292 tbai_inv . previous_tbai_invoice . order . simplified_invoice ;
277293 }
294+ } else {
295+ // ← MARK the order as invalid
296+ json . tbai_export_failed = true ;
297+ json . tbai_datas = null ;
298+ json . tbai_signature_value = null ;
278299 }
300+ } else if ( ! this . to_invoice ) {
301+ // No invoice AND not creating full invoice = REQUIRED but MISSING
302+ // This is the error case: TicketBAI simplified invoice required but not built
303+ json . tbai_export_failed = true ;
304+ json . tbai_datas = null ;
305+ json . tbai_signature_value = null ;
279306 }
280307 }
281308 return json ;
@@ -293,43 +320,75 @@ odoo.define("l10n_es_ticketbai_pos.models", function (require) {
293320 }
294321
295322 async tbai_build_invoice ( ) {
296- /*
297- * Reset a previously rejected promise to a safe resolved state.
298- * Works with both native Promises and jQuery Deferreds (still used in Odoo 16 POS).
299- */
300- var current = this . tbai_current_invoice ;
301- if ( current && typeof current . catch === "function" ) {
302- try {
303- await current ;
304- } catch ( e ) {
305- /* Absorb */
306- }
307- if (
308- ( current . state && current . state ( ) === "rejected" ) ||
309- ( current . isRejected && current . isRejected ( ) )
310- ) {
311- this . tbai_current_invoice = Promise . resolve ( null ) ;
312- }
323+ if ( this . _tbai_build_lock ) {
324+ console . warn ( "[TicketBAI] Build already in progress, waiting..." ) ;
325+ return this . tbai_current_invoice ;
313326 }
314327
315- this . tbai_current_invoice = this . tbai_current_invoice . then ( async ( ) => {
316- if ( ! this . check_tbai_conf ( ) || this . to_invoice ) {
317- return null ;
328+ this . _tbai_build_lock = true ;
329+
330+ try {
331+ var current = this . tbai_current_invoice ;
332+ if ( current && typeof current . catch === "function" ) {
333+ try {
334+ await current ;
335+ } catch ( e ) {
336+ /* Absorb */
337+ console . warn ( "[TicketBAI] Previous build failed:" , e ) ;
338+ }
339+ if (
340+ ( current . state && current . state ( ) === "rejected" ) ||
341+ ( current . isRejected && current . isRejected ( ) )
342+ ) {
343+ this . tbai_current_invoice = Promise . resolve ( null ) ;
344+ }
318345 }
319346
320- const tbai_inv = new tbai_models . TicketBAISimplifiedInvoice (
321- { } ,
322- {
323- pos : this . pos ,
324- order : this ,
347+ this . tbai_current_invoice = this . tbai_current_invoice . then (
348+ async ( ) => {
349+ if ( ! this . check_tbai_conf ( ) || this . to_invoice ) {
350+ console . info (
351+ "[TicketBAI] Skipping invoice build (conf check failed or to_invoice)"
352+ ) ;
353+ return null ;
354+ }
355+
356+ console . info ( "[TicketBAI] Starting invoice build..." ) ;
357+ const tbai_inv = new tbai_models . TicketBAISimplifiedInvoice (
358+ { } ,
359+ {
360+ pos : this . pos ,
361+ order : this ,
362+ }
363+ ) ;
364+
365+ await tbai_inv . build_invoice ( ) ;
366+ console . info (
367+ "[TicketBAI] Invoice build completed successfully"
368+ ) ;
369+ return tbai_inv ;
325370 }
326371 ) ;
327372
328- await tbai_inv . build_invoice ( ) ;
329- return tbai_inv ;
330- } ) ;
373+ return this . tbai_current_invoice ;
374+ } finally {
375+ this . _tbai_build_lock = false ;
376+ }
377+ }
378+
379+ // NEW: Validation method to check invoice state
380+ is_tbai_invoice_ready ( ) {
381+ if ( ! this . pos . company . tbai_enabled || this . to_invoice ) {
382+ // Not required
383+ return true ;
384+ }
385+
386+ const inv = this . tbai_simplified_invoice ;
387+ if ( ! inv ) {
388+ return false ;
389+ }
331390
332- return this . tbai_current_invoice ;
391+ return Boolean ( inv . datas && inv . signature_value && inv . tbai_identifier ) ;
333392 }
334393 } ;
335394 Registries . Model . extend ( Order , L10nEsTicketBAIPosOrder ) ;
0 commit comments