Skip to content

Commit 91b0623

Browse files
committed
[IMP] point_of_sale: real time syncing
We use the built-in listener of the related records to keep track of all of the changes. We then queue these changes and send them to the backend automatically. This means that the pos ui developer never needs to think about syncing their data to the server. It is done automatically. `sync_from_ui` is now removed. It performed a couple of actions: 1. Prepared the vals -> this is now done in `create` or `write` where needed 2. Performed side effects -> side effects are now moved in `action_pos_order_paid`, which is called by the client after validation. 3. It was used as a notifier -> modules that need to be notified of order creation and update can hook into `create` and `write`
1 parent addbd42 commit 91b0623

File tree

135 files changed

+1494
-3555
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+1494
-3555
lines changed

addons/point_of_sale/models/pos_config.py

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from odoo.http import request
1111
from odoo.exceptions import AccessError, ValidationError, UserError
1212
from odoo.tools import SQL, convert
13-
from odoo.osv import expression
1413

1514
DEFAULT_LIMIT_LOAD_PRODUCT = 5000
1615
DEFAULT_LIMIT_LOAD_PARTNER = 100
@@ -202,49 +201,11 @@ def _get_default_tip_product(self):
202201
last_data_change = fields.Datetime(string='Last Write Date', readonly=True, compute='_compute_local_data_integrity', store=True)
203202
fallback_nomenclature_id = fields.Many2one('barcode.nomenclature', string="Fallback Nomenclature")
204203

205-
def notify_synchronisation(self, session_id, login_number, records={}):
206-
self.ensure_one()
207-
static_records = {}
208-
209-
for model, ids in records.items():
210-
static_records[model] = self.env[model]._read_pos_record(ids, self.id)
211-
212-
self._notify('SYNCHRONISATION', {
213-
'static_records': static_records,
214-
'session_id': session_id,
215-
'login_number': login_number,
216-
'records': records
217-
})
218-
219-
def read_config_open_orders(self, domain, record_ids=[]):
220-
delete_record_ids = {}
221-
dynamic_records = {}
222-
223-
for model, domain in domain.items():
224-
ids = record_ids[model]
225-
delete_record_ids[model] = [id for id in ids if not self.env[model].browse(id).exists()]
226-
dynamic_records[model] = self.env[model].search(domain)
227-
228-
pos_order_data = dynamic_records.get('pos.order') or self.env['pos.order']
229-
data = pos_order_data.read_pos_data([], self.id)
230-
231-
for key, records in dynamic_records.items():
232-
fields = self.env[key]._load_pos_data_fields(self.id)
233-
ids = list(set(records.ids + [record['id'] for record in data.get(key, [])]))
234-
dynamic_records[key] = self.env[key].browse(ids).read(fields, load=False)
235-
236-
for key, value in data.items():
237-
if key not in dynamic_records:
238-
dynamic_records[key] = value
239-
240-
return {
241-
'dynamic_records': dynamic_records,
242-
'deleted_record_ids': delete_record_ids,
243-
}
244-
245204
@api.model
246205
def _load_pos_data_domain(self, data):
247-
return [('id', '=', data['pos.session'][0]['config_id'])]
206+
main_config = self.env['pos.config'].browse(data['pos.session'][0]['config_id'])
207+
other_configs = main_config._configs_that_share_data()
208+
return [('id', 'in', [main_config.id] + other_configs.ids)]
248209

249210
def _load_pos_data(self, data):
250211
domain = self._load_pos_data_domain(data)
@@ -258,6 +219,58 @@ def _post_read_pos_data(self, data):
258219
data[0]['_IS_VAT'] = self.env.company.country_id.id in self.env.ref("base.europe").country_ids.ids
259220
return super()._post_read_pos_data(data)
260221

222+
def _configs_that_share_data(self):
223+
self.ensure_one()
224+
return self.trusted_config_ids
225+
226+
def notify_data_change(self, queue, login_number, id_updates):
227+
if not self:
228+
# No config is available to notify in certain scenarios (e.g., pre-display)
229+
return
230+
for config in self._configs_that_share_data() + self:
231+
config._notify('DATA_CHANGED', {'queue': queue, 'login_number': login_number, 'config_id': self.id, 'id_updates': id_updates})
232+
233+
def flush(self, queue, login_number):
234+
235+
def get_record(model, id):
236+
return self.env[model].search([('uuid', '=', id)], limit=1) if isinstance(id, str) else self.env[model].browse(id)
237+
238+
def replace_uuid_with_id(model, vals):
239+
def adapt_value(model, key, value):
240+
match self.env[model]._fields[key].type:
241+
case "many2one":
242+
return get_record(self.env[model]._fields[key].comodel_name, value).id
243+
case "many2many" | "one2many":
244+
return [(6, 0, [get_record(self.env[model]._fields[key].comodel_name, val).id for val in value])]
245+
case _:
246+
return value
247+
return {key: adapt_value(model, key, value) for key, value in vals.items()}
248+
249+
id_updates = {}
250+
251+
for [operation, *data] in queue:
252+
match operation:
253+
case "CREATE":
254+
model, vals = data
255+
vals = {key: val for key, val in vals.items() if key[0] != '_'}
256+
new_record = self.env[model].create([replace_uuid_with_id(model, vals)])
257+
id_updates[vals['uuid']] = new_record.id
258+
case "UPDATE":
259+
model, id, vals = data
260+
vals = {key: val for key, val in vals.items() if key[0] != '_'}
261+
record = get_record(model, id)
262+
record.write(replace_uuid_with_id(model, vals))
263+
case "DELETE":
264+
model, id = data
265+
record = get_record(model, id)
266+
if record.exists():
267+
record.unlink()
268+
case _:
269+
pass
270+
271+
self.notify_data_change(queue, login_number, id_updates)
272+
return id_updates
273+
261274
@api.depends('payment_method_ids')
262275
def _compute_cash_control(self):
263276
for config in self:

0 commit comments

Comments
 (0)