7
7
"fmt"
8
8
"strconv"
9
9
"strings"
10
+ "time"
10
11
11
12
"github.com/google/uuid"
12
13
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -31,6 +32,7 @@ var metaUIDNamespace = uuid.MustParse("51887759-C769-4829-9910-BB9D5F92767D")
31
32
var roleAccountFilter = []any {"category_id" , "in" , []int {roleAccountCategory }}
32
33
var activeFilter = []any {"active" , "in" , []bool {true }}
33
34
var notInflightFilter = []any {"x_control_api_inflight" , "in" , []any {false }}
35
+ var mustInflightFilter = []any {"x_control_api_inflight" , "not in" , []any {false }}
34
36
35
37
var (
36
38
// There's a ton of fields we don't want to override in Odoo.
@@ -63,22 +65,36 @@ type Config struct {
63
65
PaymentTermID int
64
66
}
65
67
66
- func NewOdoo8Storage (odooURL string , debugTransport bool , conf Config ) odoo.OdooStorage {
67
- return & oodo8Storage {
68
+ var _ odoo.OdooStorage = & Odoo8Storage {}
69
+
70
+ func NewOdoo8Storage (odooURL string , debugTransport bool , conf Config ) * Odoo8Storage {
71
+ return & Odoo8Storage {
68
72
config : conf ,
69
73
sessionCreator : func (ctx context.Context ) (client.QueryExecutor , error ) {
70
74
return client .Open (ctx , odooURL , client.ClientOptions {UseDebugLogger : debugTransport })
71
75
},
72
76
}
73
77
}
74
78
75
- type oodo8Storage struct {
79
+ func NewFailedRecordScrubber (odooURL string , debugTransport bool ) * FailedRecordScrubber {
80
+ return & FailedRecordScrubber {
81
+ sessionCreator : func (ctx context.Context ) (client.QueryExecutor , error ) {
82
+ return client .Open (ctx , odooURL , client.ClientOptions {UseDebugLogger : debugTransport })
83
+ },
84
+ }
85
+ }
86
+
87
+ type Odoo8Storage struct {
76
88
config Config
77
89
78
90
sessionCreator func (ctx context.Context ) (client.QueryExecutor , error )
79
91
}
80
92
81
- func (s * oodo8Storage ) Get (ctx context.Context , name string ) (* billingv1.BillingEntity , error ) {
93
+ type FailedRecordScrubber struct {
94
+ sessionCreator func (ctx context.Context ) (client.QueryExecutor , error )
95
+ }
96
+
97
+ func (s * Odoo8Storage ) Get (ctx context.Context , name string ) (* billingv1.BillingEntity , error ) {
82
98
company , accountingContact , err := s .get (ctx , name )
83
99
if err != nil {
84
100
return nil , err
@@ -88,7 +104,7 @@ func (s *oodo8Storage) Get(ctx context.Context, name string) (*billingv1.Billing
88
104
return & be , nil
89
105
}
90
106
91
- func (s * oodo8Storage ) get (ctx context.Context , name string ) (company model.Partner , accountingContact model.Partner , err error ) {
107
+ func (s * Odoo8Storage ) get (ctx context.Context , name string ) (company model.Partner , accountingContact model.Partner , err error ) {
92
108
id , err := k8sIDToOdooID (name )
93
109
if err != nil {
94
110
return model.Partner {}, model.Partner {}, err
@@ -117,7 +133,7 @@ func (s *oodo8Storage) get(ctx context.Context, name string) (company model.Part
117
133
return company , accountingContact , nil
118
134
}
119
135
120
- func (s * oodo8Storage ) List (ctx context.Context ) ([]billingv1.BillingEntity , error ) {
136
+ func (s * Odoo8Storage ) List (ctx context.Context ) ([]billingv1.BillingEntity , error ) {
121
137
l := klog .FromContext (ctx )
122
138
123
139
session , err := s .sessionCreator (ctx )
@@ -173,7 +189,7 @@ func (s *oodo8Storage) List(ctx context.Context) ([]billingv1.BillingEntity, err
173
189
return bes , nil
174
190
}
175
191
176
- func (s * oodo8Storage ) Create (ctx context.Context , be * billingv1.BillingEntity ) error {
192
+ func (s * Odoo8Storage ) Create (ctx context.Context , be * billingv1.BillingEntity ) error {
177
193
l := klog .FromContext (ctx )
178
194
179
195
if be == nil {
@@ -225,7 +241,7 @@ func (s *oodo8Storage) Create(ctx context.Context, be *billingv1.BillingEntity)
225
241
return nil
226
242
}
227
243
228
- func (s * oodo8Storage ) Update (ctx context.Context , be * billingv1.BillingEntity ) error {
244
+ func (s * Odoo8Storage ) Update (ctx context.Context , be * billingv1.BillingEntity ) error {
229
245
l := klog .FromContext (ctx )
230
246
231
247
if be == nil {
@@ -274,6 +290,42 @@ func (s *oodo8Storage) Update(ctx context.Context, be *billingv1.BillingEntity)
274
290
return nil
275
291
}
276
292
293
+ // CleanupIncompleteRecords looks for partner records in Odoo that still have the "inflight" flag set despite being older than `minAge`. Those records are then deleted.
294
+ // Such records might come into existence due to a partially failed creation request.
295
+ func (s * FailedRecordScrubber ) CleanupIncompleteRecords (ctx context.Context , minAge time.Duration ) error {
296
+ l := klog .FromContext (ctx )
297
+ l .Info ("Looking for stale inflight partner records..." )
298
+
299
+ session , err := s .sessionCreator (ctx )
300
+ if err != nil {
301
+ return err
302
+ }
303
+ o := model .NewOdoo (session )
304
+
305
+ inflightRecords , err := o .SearchPartners (ctx , []client.Filter {
306
+ mustInflightFilter ,
307
+ })
308
+ if err != nil {
309
+ return err
310
+ }
311
+
312
+ ids := []int {}
313
+
314
+ for _ , record := range inflightRecords {
315
+ createdTime := record .CreationTimestamp .ToTime ()
316
+
317
+ if createdTime .Before (time .Now ().Add (- 1 * minAge )) {
318
+ ids = append (ids , record .ID )
319
+ l .Info ("Preparing to delete inflight partner record" , "name" , record .Name , "id" , record .ID )
320
+ }
321
+ }
322
+
323
+ if len (ids ) != 0 {
324
+ return o .DeletePartner (ctx , ids )
325
+ }
326
+ return nil
327
+ }
328
+
277
329
func k8sIDToOdooID (id string ) (int , error ) {
278
330
if ! strings .HasPrefix (id , "be-" ) {
279
331
return 0 , fmt .Errorf ("invalid ID, missing prefix: %s" , id )
0 commit comments