@controleonline/ui-common 1.2.87 → 1.2.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.github/workflows/integration-source-gate.yml +68 -0
  2. package/.github/workflows/manager-worker.yml +7 -156
  3. package/.github/workflows/publish-npm.yml +73 -27
  4. package/.github/workflows/pull-request-checks.yml +7 -5
  5. package/docs/technical/Device-Detail-Alias-UI-Sync.md +141 -0
  6. package/package.json +13 -8
  7. package/src/api/localDB.js +86 -131
  8. package/src/react/components/BottomNavigationBar.js +178 -174
  9. package/src/react/components/BottomNavigationBar.styles.js +139 -118
  10. package/src/react/components/DefaultProvider.native.js +427 -421
  11. package/src/react/components/DefaultProvider.web.js +131 -125
  12. package/src/react/components/RemoteCheckoutService.js +3 -3
  13. package/src/react/components/RuntimeFooterMarqueeText.js +88 -36
  14. package/src/react/components/RuntimeInfoFooter.js +76 -43
  15. package/src/react/components/RuntimeInfoFooter.styles.js +22 -14
  16. package/src/react/config/deviceConfigBootstrap.js +71 -233
  17. package/src/react/config/deviceConfigValues.js +238 -0
  18. package/src/react/pages/Devices/detail/DeviceChargePermissionSection.js +32 -0
  19. package/src/react/pages/Devices/detail/DeviceDetailAlertsCommandsSection.js +4 -22
  20. package/src/react/pages/Devices/detail/DeviceDetailMovementSections.js +6 -2
  21. package/src/react/pages/Devices/detail/DeviceDetailOrdersPrintSection.js +4 -0
  22. package/src/react/pages/Devices/detail/DeviceDetailPdvConfigSection.js +7 -0
  23. package/src/react/pages/Devices/detail/DeviceDetailScreen.js +24 -335
  24. package/src/react/pages/Devices/detail/createDeviceDetailContext.js +20 -0
  25. package/src/react/pages/Devices/detail/deviceDetailHelpers.js +2 -11
  26. package/src/react/pages/Devices/detail/useDeviceDetailActions.js +11 -36
  27. package/src/react/pages/Devices/detail/useDeviceDetailLoaders.js +129 -37
  28. package/src/react/pages/Devices/detail/useDeviceDetailSaves.js +11 -38
  29. package/src/react/pages/Devices/detail/useDeviceDetailStateA.js +9 -28
  30. package/src/react/pages/IntegrationConfigFields.js +1 -0
  31. package/src/react/pages/IntegrationConfigPage.js +101 -45
  32. package/src/react/pages/IntegrationConfigPage.utils.js +21 -13
  33. package/src/react/pages/fetchPeopleConfigs.js +21 -0
  34. package/src/react/pages/integrationsCatalog.js +15 -110
  35. package/src/react/services/Cielo/Cielo.js +9 -9
  36. package/src/react/services/Getnet/Getnet.web.js +25 -0
  37. package/src/react/utils/companyAuthority.js +36 -0
  38. package/src/react/utils/confirmAction.js +12 -0
  39. package/src/react/utils/fileUrl.js +14 -5
  40. package/src/react/utils/paymentDevices.js +375 -270
  41. package/src/react/utils/printerDevices.js +2 -2
  42. package/src/react/utils/runtimeFooter.js +178 -224
  43. package/src/react/utils/runtimeFooterText.js +160 -0
  44. package/src/react/utils/runtimeLanguage.js +5 -5
  45. package/src/react/utils/runtimeZoom.js +29 -5
  46. package/src/react/utils/shopFranchises.js +167 -12
  47. package/src/react/utils/storeColumns.js +235 -217
  48. package/src/react/utils/systemErrorMessage.js +2 -1
  49. package/src/store/integration/index.js +43 -0
  50. package/src/tests/browser/manager/device-detail-apply-config-defined.spec.js +31 -0
  51. package/src/tests/browser/manager/device-detail-composition.spec.js +295 -0
  52. package/src/tests/browser/manager/device-detail-copy-config.spec.js +11 -1
  53. package/src/tests/browser/manager/device-detail-pdv-current-company.spec.js +273 -0
  54. package/src/tests/browser/manager/runtime-footer-rotation.spec.js +397 -0
  55. package/src/tests/react/components/DefaultProviderAuthority.test.js +28 -0
  56. package/src/tests/react/components/RuntimeFooterMarqueeText.build.test.js +29 -0
  57. package/src/tests/react/config/deviceConfigBootstrap.test.js +24 -1
  58. package/src/tests/react/pages/DeviceChargePermissionSection.test.js +42 -0
  59. package/src/tests/react/pages/applyCurrentDeviceConfig.test.js +85 -0
  60. package/src/tests/react/pages/deviceDetailComposition.test.js +65 -0
  61. package/src/tests/react/pages/deviceDetailHookDependencies.test.mjs +31 -0
  62. package/src/tests/react/pages/deviceDetailLoaderDependencies.test.mjs +28 -0
  63. package/src/tests/react/pages/receitaFederalIntegrationContract.test.mjs +27 -6
  64. package/src/tests/react/services/Cielo.test.js +2 -2
  65. package/src/tests/react/utils/companyAuthority.test.js +53 -0
  66. package/src/tests/react/utils/formatter.test.mjs +6 -0
  67. package/src/tests/react/utils/paymentDevices.test.js +72 -0
  68. package/src/tests/react/utils/runtimeFooter.test.js +35 -0
  69. package/src/tests/react/utils/runtimeFooterText.test.js +88 -0
  70. package/src/tests/react/utils/runtimeLanguage.test.js +4 -4
  71. package/src/tests/react/utils/runtimeZoom.test.js +20 -0
  72. package/src/tests/react/utils/shopFranchises.test.js +32 -13
  73. package/src/tests/react/utils/translate.test.mjs +6 -6
  74. package/src/utils/formatter.js +7 -3
  75. package/src/utils/translate.js +16 -16
  76. package/src/utils/translatePending.js +6 -6
  77. package/src/utils/translateStorage.js +2 -2
@@ -7,10 +7,10 @@ import React, {
7
7
  useRef,
8
8
  useState,
9
9
  } from 'react';
10
- import {View, AppState, Platform} from 'react-native';
11
- import DeviceInfo from 'react-native-device-info';
12
- import Translate from '@controleonline/ui-common/src/utils/translate';
13
- import {WebsocketListener} from '@controleonline/ui-common/src/react/components/WebsocketListener';
10
+ import {View, AppState, Platform, useWindowDimensions} from 'react-native';
11
+ import DeviceInfo from 'react-native-device-info';
12
+ import Translate from '@controleonline/ui-common/src/utils/translate';
13
+ import {WebsocketListener} from '@controleonline/ui-common/src/react/components/WebsocketListener';
14
14
  import BackgroundRuntimeBridge from '@controleonline/ui-common/src/react/components/BackgroundRuntimeBridge';
15
15
  import DeviceAlertSoundService from '@controleonline/ui-common/src/react/components/DeviceAlertSoundService';
16
16
  import KioskModeBridge from '@controleonline/ui-common/src/react/components/KioskModeBridge';
@@ -18,7 +18,7 @@ import LauncherModeBridge from '@controleonline/ui-common/src/react/components/L
18
18
  import DeliveryPushBridge from '@controleonline/ui-common/src/react/components/DeliveryPushBridge';
19
19
  import ManagerPushBridge from '@controleonline/ui-common/src/react/components/ManagerPushBridge';
20
20
  import PrintService from '@controleonline/ui-common/src/react/components/PrintService';
21
- import RemoteCheckoutService from '@controleonline/ui-common/src/react/components/RemoteCheckoutService';
21
+ import RemoteCheckoutService from '@controleonline/ui-common/src/react/components/RemoteCheckoutService';
22
22
  import ProductCatalogCacheService from '@controleonline/ui-common/src/react/components/ProductCatalogCacheService';
23
23
  import RuntimeInfoFooter from '@controleonline/ui-common/src/react/components/RuntimeInfoFooter';
24
24
  import {isPublicRoute} from '../router/publicRoutes';
@@ -30,16 +30,18 @@ const {
30
30
  isTranslationBootstrapReady,
31
31
  resolveConfiguredLanguage,
32
32
  } = require('../utils/runtimeLanguage');
33
- import {
34
- applyPaletteToRuntimeColors,
35
- applyThemeCssVariables,
36
- resolveThemePalette,
37
- } from '@controleonline/../../src/styles/branding';
38
- import {colors as runtimeColors} from '@controleonline/../../src/styles/colors';
39
- import {
40
- buildProviderManagedDeviceConfigs,
41
- parseConfigsObject,
42
- } from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
33
+ import {
34
+ applyPaletteToRuntimeColors,
35
+ applyThemeCssVariables,
36
+ resolveThemePalette,
37
+ } from '@controleonline/../../src/styles/branding';
38
+ import {colors as runtimeColors} from '@controleonline/../../src/styles/colors';
39
+ import {
40
+ buildDefaultDeviceConfigs,
41
+ buildProviderManagedDeviceConfigs,
42
+ parseConfigsObject,
43
+ } from '@controleonline/ui-common/src/react/config/deviceConfigBootstrap';
44
+ import {canAdministerCompany} from '@controleonline/ui-common/src/react/utils/companyAuthority';
43
45
  import {
44
46
  buildDeviceRegistrationPayload,
45
47
  buildLocalRuntimeDevice,
@@ -48,7 +50,9 @@ import {
48
50
  } from '@controleonline/ui-common/src/react/utils/deviceRuntime';
49
51
  import {
50
52
  filterWalletPaymentTypesByAllowedIds,
53
+ getPaymentGateway,
51
54
  resolveDevicePaymentTypeIds,
55
+ selectPosWalletPaymentTypes,
52
56
  } from '@controleonline/ui-common/src/react/utils/paymentDevices';
53
57
  import {
54
58
  normalizeRuntimeMenuResponse,
@@ -58,29 +62,29 @@ import {
58
62
  } from '@controleonline/ui-common/src/react/utils/runtimeZoom';
59
63
  import packageJson from '@package';
60
64
  import providerStyles from './DefaultProvider.styles';
61
-
62
- import {useStore} from '@store';
63
- import stores from '@stores';
64
- const ThemeContext = createContext();
65
-
66
- const parseThemeCss = cssText => {
67
- const parsedColors = {};
68
- const matches = String(cssText || '').match(/--([\w-]+)\s*:\s*([^;}{]+)\s*;/g) || [];
69
-
70
- matches.forEach(match => {
71
- const clean = match.trim().replace(/^--/, '').replace(/;$/, '');
72
- const splitIndex = clean.indexOf(':');
73
- if (splitIndex <= 0) return;
74
-
75
- const key = clean.slice(0, splitIndex).trim();
76
- const value = clean.slice(splitIndex + 1).trim();
77
- if (!key || !value) return;
78
- parsedColors[key] = value;
79
- });
80
-
81
- return parsedColors;
82
- };
83
-
65
+
66
+ import {useStore} from '@store';
67
+ import stores from '@stores';
68
+ const ThemeContext = createContext();
69
+
70
+ const parseThemeCss = cssText => {
71
+ const parsedColors = {};
72
+ const matches = String(cssText || '').match(/--([\w-]+)\s*:\s*([^;}{]+)\s*;/g) || [];
73
+
74
+ matches.forEach(match => {
75
+ const clean = match.trim().replace(/^--/, '').replace(/;$/, '');
76
+ const splitIndex = clean.indexOf(':');
77
+ if (splitIndex <= 0) return;
78
+
79
+ const key = clean.slice(0, splitIndex).trim();
80
+ const value = clean.slice(splitIndex + 1).trim();
81
+ if (!key || !value) return;
82
+ parsedColors[key] = value;
83
+ });
84
+
85
+ return parsedColors;
86
+ };
87
+
84
88
  const normalizeEntityId = value =>
85
89
  String(value?.id || value || '')
86
90
  .replace(/\D/g, '')
@@ -103,56 +107,41 @@ const resolveDeviceConfigPeopleIri = ({appType, currentCompany, user}) => {
103
107
 
104
108
  return '';
105
109
  };
106
- const isTenantAdministrativeAuthority = company => {
107
- const userFlags = company?.user || {};
108
- return !!(
109
- userFlags.owner_enabled ||
110
- userFlags.director_enabled ||
111
- userFlags.manager_enabled ||
112
- userFlags.admin_enabled ||
113
- company?.owner_enabled ||
114
- company?.director_enabled ||
115
- company?.manager_enabled
116
- );
117
- };
118
-
119
-
120
-
121
110
  export const DefaultProvider = ({
122
111
  children,
123
112
  currentRouteName = '',
124
113
  onBootstrapReady,
125
114
  }) => {
126
- const appType = String(app_type || '').toUpperCase();
127
- const isShopClientApp = appType === 'SHOP';
128
- const themeStore = useStore('theme');
129
- const getters = themeStore.getters;
130
- const actions = themeStore.actions;
131
- const authStore = useStore('auth');
132
- const authGetters = authStore.getters;
133
- const peopleStore = useStore('people');
134
- const peopleGetters = peopleStore.getters;
135
- const peopleActions = peopleStore.actions;
136
-
137
- const deviceStore = useStore('device');
138
- const deviceActions = deviceStore.actions;
139
-
140
- const device_configStore = useStore('device_config');
141
- const deviceConfigsGetters = device_configStore.getters;
142
- const deviceConfigsActions = device_configStore.actions;
143
-
144
- const configsStore = useStore('configs');
145
- const configActions = configsStore.actions;
146
- const configsGetters = configsStore.getters;
147
- const printerStore = useStore('printer');
148
- const printerActions = printerStore.actions;
115
+ const appType = String(app_type || '').toUpperCase();
116
+ const isShopClientApp = appType === 'SHOP';
117
+ const themeStore = useStore('theme');
118
+ const getters = themeStore.getters;
119
+ const actions = themeStore.actions;
120
+ const authStore = useStore('auth');
121
+ const authGetters = authStore.getters;
122
+ const peopleStore = useStore('people');
123
+ const peopleGetters = peopleStore.getters;
124
+ const peopleActions = peopleStore.actions;
125
+
126
+ const deviceStore = useStore('device');
127
+ const deviceActions = deviceStore.actions;
128
+
129
+ const device_configStore = useStore('device_config');
130
+ const deviceConfigsGetters = device_configStore.getters;
131
+ const deviceConfigsActions = device_configStore.actions;
132
+
133
+ const configsStore = useStore('configs');
134
+ const configActions = configsStore.actions;
135
+ const configsGetters = configsStore.getters;
136
+ const printerStore = useStore('printer');
137
+ const printerActions = printerStore.actions;
149
138
  const walletPaymentTypeStore = useStore('walletPaymentType');
150
139
  const paymentTypeActions = walletPaymentTypeStore.actions;
151
140
  const translateStore = useStore('translate');
152
141
  const translateActions = translateStore.actions;
153
142
  const {items: companyConfigs} = configsGetters;
154
- const {colors, menus} = getters;
155
- const {currentCompany, defaultCompany, companies} = peopleGetters;
143
+ const {colors, menus} = getters;
144
+ const {currentCompany, mainCompany, companies} = peopleGetters;
156
145
  const {item: device_config} = deviceConfigsGetters;
157
146
  const {isLogged, sessionChecked, user} = authGetters;
158
147
  const hasCurrentCompany =
@@ -160,8 +149,8 @@ export const DefaultProvider = ({
160
149
  const [translateReady, setTranslateReady] = useState(false);
161
150
  const [activeTranslateBootstrapKey, setActiveTranslateBootstrapKey] =
162
151
  useState('');
163
- const [deviceConfigFetched, setDeviceConfigFetched] = useState(false);
164
- const [mainConfigsDiscovered, setMainConfigsDiscovered] = useState(false);
152
+ const [deviceConfigFetched, setDeviceConfigFetched] = useState(false);
153
+ const [mainConfigsDiscovered, setMainConfigsDiscovered] = useState(false);
165
154
  const [deviceRuntimeConfigSynced, setDeviceRuntimeConfigSynced] =
166
155
  useState(false);
167
156
  const [appState, setAppState] = useState(AppState.currentState || 'active');
@@ -173,15 +162,19 @@ export const DefaultProvider = ({
173
162
  const [device, setDevice] = useState(
174
163
  JSON.parse(localStorage.getItem('device') || '{}'),
175
164
  );
176
- const packageVersion = packageJson?.version || packageJson?.default?.version;
165
+ const packageVersion = packageJson?.version || packageJson?.default?.version;
177
166
  const appVersion = packageVersion || device?.appVersion;
178
167
  const runtimeDeviceType = resolveOperationalDeviceType({
179
168
  appType: app_type,
180
169
  deviceInfo: device || {},
181
170
  });
171
+ const {width: windowWidth, height: windowHeight} = useWindowDimensions();
182
172
  const runtimeUiScaleStyle = useMemo(
183
- () => buildRuntimeZoomStyle(APP_ENV?.ZOOM),
184
- [],
173
+ () =>
174
+ buildRuntimeZoomStyle(APP_ENV?.ZOOM, {
175
+ viewport: {width: windowWidth, height: windowHeight},
176
+ }),
177
+ [windowHeight, windowWidth],
185
178
  );
186
179
  const deviceConfigPeopleIri = resolveDeviceConfigPeopleIri({
187
180
  appType,
@@ -197,7 +190,7 @@ export const DefaultProvider = ({
197
190
  );
198
191
  const configuredTranslationLanguage = resolveConfiguredLanguage({
199
192
  currentCompany,
200
- defaultCompany,
193
+ mainCompany,
201
194
  currentConfig: currentTranslationConfig,
202
195
  sessionData: currentTranslationSession,
203
196
  });
@@ -205,7 +198,7 @@ export const DefaultProvider = ({
205
198
  ? buildTranslationBootstrapKey({
206
199
  language: configuredTranslationLanguage,
207
200
  currentCompanyId: normalizeEntityId(currentCompany?.id),
208
- defaultCompanyId: normalizeEntityId(defaultCompany?.id),
201
+ mainCompanyId: normalizeEntityId(mainCompany?.id),
209
202
  })
210
203
  : '';
211
204
  const requiresTranslateBootstrap = Boolean(
@@ -246,7 +239,7 @@ export const DefaultProvider = ({
246
239
  : {
247
240
  appVersion,
248
241
  colors,
249
- defaultCompany,
242
+ mainCompany,
250
243
  device,
251
244
  },
252
245
  bottomChrome: {
@@ -258,7 +251,7 @@ export const DefaultProvider = ({
258
251
  appVersion,
259
252
  bottomNavigationCount,
260
253
  colors,
261
- defaultCompany,
254
+ mainCompany,
262
255
  device,
263
256
  isShopClientApp,
264
257
  menus,
@@ -274,19 +267,19 @@ export const DefaultProvider = ({
274
267
  <ManagerPushBridge device={device} setDevice={setDevice} />
275
268
  </>
276
269
  );
277
-
278
- useEffect(() => {
279
- global.refreshTranslationsUI = () => {
280
- setTranslateVersion(version => version + 1);
281
- };
282
-
283
- return () => {
284
- if (global.refreshTranslationsUI) {
285
- delete global.refreshTranslationsUI;
286
- }
287
- };
288
- }, []);
289
-
270
+
271
+ useEffect(() => {
272
+ global.refreshTranslationsUI = () => {
273
+ setTranslateVersion(version => version + 1);
274
+ };
275
+
276
+ return () => {
277
+ if (global.refreshTranslationsUI) {
278
+ delete global.refreshTranslationsUI;
279
+ }
280
+ };
281
+ }, []);
282
+
290
283
  useEffect(() => {
291
284
  if (isLogged && !isPublicRouteActive) {
292
285
  return;
@@ -302,80 +295,80 @@ export const DefaultProvider = ({
302
295
  delete global.t;
303
296
  }
304
297
  }, [isLogged, isPublicRouteActive, translateActions]);
305
-
306
- useEffect(() => {
307
- const subscription = AppState.addEventListener('change', nextState => {
308
- setAppState(nextState || 'active');
309
- });
310
-
311
- return () => {
312
- subscription?.remove?.();
313
- };
314
- }, []);
315
-
316
- const fetchDeviceId = async () => {
317
- const uniqueId = await DeviceInfo.getUniqueId();
318
- const deviceId = await DeviceInfo.getDeviceId();
319
- const appName = DeviceInfo.getApplicationName();
320
- const systemName = await DeviceInfo.getSystemName();
321
- const systemVersion = await DeviceInfo.getSystemVersion();
322
- const manufacturer = await DeviceInfo.getManufacturer();
323
- const model = await DeviceInfo.getModel();
324
- const batteryLevel = await DeviceInfo.getBatteryLevel();
325
- const isEmulator = await DeviceInfo.isEmulator();
326
- const appVersion = await DeviceInfo.getVersion();
327
- const buildNumber = await DeviceInfo.getBuildNumber();
328
- let ld = null;
329
- if (uniqueId) {
330
- ld = buildLocalRuntimeDevice({
331
- appType: app_type,
332
- deviceInfo: {
333
- id: uniqueId,
334
- appName: appName,
335
- deviceType: deviceId,
336
- systemName: systemName,
337
- systemVersion: systemVersion,
338
- manufacturer: manufacturer,
339
- model: model,
340
- batteryLevel: batteryLevel,
341
- isEmulator: isEmulator,
342
- appVersion: appVersion,
343
- buildNumber: buildNumber,
344
- metadata: device?.metadata,
345
- },
346
- });
347
- setDevice(ld);
348
- localStorage.setItem('device', JSON.stringify(ld));
349
- } else {
350
- setTimeout(() => {
351
- fetchDeviceId();
352
- }, 300);
353
- }
354
- };
355
- useEffect(() => {
356
- const checkVersion = async () => {
357
- const appVersion = await DeviceInfo.getVersion();
358
- if (
359
- device &&
360
- ((device.appVersion && device.appVersion != appVersion) || !device.appName)
361
- ) {
362
- fetchDeviceId();
363
- }
364
- };
365
- checkVersion();
366
- }, [device]);
367
-
368
- useEffect(() => {
369
- if (!device || !device.id) {
370
- fetchDeviceId();
371
- } else {
372
- deviceActions.setItem(device);
373
- }
374
- }, [device]);
375
-
298
+
299
+ useEffect(() => {
300
+ const subscription = AppState.addEventListener('change', nextState => {
301
+ setAppState(nextState || 'active');
302
+ });
303
+
304
+ return () => {
305
+ subscription?.remove?.();
306
+ };
307
+ }, []);
308
+
309
+ const fetchDeviceId = async () => {
310
+ const uniqueId = await DeviceInfo.getUniqueId();
311
+ const deviceId = await DeviceInfo.getDeviceId();
312
+ const appName = DeviceInfo.getApplicationName();
313
+ const systemName = await DeviceInfo.getSystemName();
314
+ const systemVersion = await DeviceInfo.getSystemVersion();
315
+ const manufacturer = await DeviceInfo.getManufacturer();
316
+ const model = await DeviceInfo.getModel();
317
+ const batteryLevel = await DeviceInfo.getBatteryLevel();
318
+ const isEmulator = await DeviceInfo.isEmulator();
319
+ const appVersion = await DeviceInfo.getVersion();
320
+ const buildNumber = await DeviceInfo.getBuildNumber();
321
+ let ld = null;
322
+ if (uniqueId) {
323
+ ld = buildLocalRuntimeDevice({
324
+ appType: app_type,
325
+ deviceInfo: {
326
+ id: uniqueId,
327
+ appName: appName,
328
+ deviceType: deviceId,
329
+ systemName: systemName,
330
+ systemVersion: systemVersion,
331
+ manufacturer: manufacturer,
332
+ model: model,
333
+ batteryLevel: batteryLevel,
334
+ isEmulator: isEmulator,
335
+ appVersion: appVersion,
336
+ buildNumber: buildNumber,
337
+ metadata: device?.metadata,
338
+ },
339
+ });
340
+ setDevice(ld);
341
+ localStorage.setItem('device', JSON.stringify(ld));
342
+ } else {
343
+ setTimeout(() => {
344
+ fetchDeviceId();
345
+ }, 300);
346
+ }
347
+ };
348
+ useEffect(() => {
349
+ const checkVersion = async () => {
350
+ const appVersion = await DeviceInfo.getVersion();
351
+ if (
352
+ device &&
353
+ ((device.appVersion && device.appVersion != appVersion) || !device.appName)
354
+ ) {
355
+ fetchDeviceId();
356
+ }
357
+ };
358
+ checkVersion();
359
+ }, [device]);
360
+
361
+ useEffect(() => {
362
+ if (!device || !device.id) {
363
+ fetchDeviceId();
364
+ } else {
365
+ deviceActions.setItem(device);
366
+ }
367
+ }, [device]);
368
+
376
369
  useEffect(() => {
377
370
  if (device && device.id) {
378
- peopleActions.defaultCompany();
371
+ peopleActions.mainCompany();
379
372
  }
380
373
  }, [device?.id]);
381
374
 
@@ -398,6 +391,15 @@ export const DefaultProvider = ({
398
391
  return;
399
392
  }
400
393
 
394
+ // Device registration changes tenant context and is not valid without an
395
+ // active company or administrative authority for that company.
396
+ if (
397
+ !currentCompany?.id ||
398
+ !canAdministerCompany({company: currentCompany, mainCompany, user})
399
+ ) {
400
+ return;
401
+ }
402
+
401
403
  let cancelled = false;
402
404
 
403
405
  const syncDeviceRegistration = async () => {
@@ -406,92 +408,95 @@ export const DefaultProvider = ({
406
408
  people: deviceConfigPeopleIri,
407
409
  type: runtimeDeviceType,
408
410
  });
409
-
410
- if (cancelled) {
411
- return;
412
- }
413
-
414
- const existingDevice =
415
- Array.isArray(items) && items.length > 0 ? items[0] : null;
416
- const nextDevice = buildDeviceRegistrationPayload({
417
- deviceInfo: device,
418
- appType: app_type,
419
- existingDevice,
420
- });
421
-
422
- if (!hasDeviceRecordChanges({existingDevice, nextDevice})) {
423
- const nextLocalDevice = {
424
- ...device,
425
- entityId: existingDevice?.id || device?.entityId || null,
426
- entityIri:
427
- existingDevice?.['@id'] ||
428
- device?.entityIri ||
429
- (existingDevice?.id ? `/devices/${existingDevice.id}` : null),
430
- alias: existingDevice?.alias || nextDevice.alias,
431
- type: device?.type || runtimeDeviceType,
432
- metadata: {
433
- ...(existingDevice?.metadata || {}),
434
- ...(nextDevice.metadata || {}),
435
- },
436
- };
437
-
438
- if (JSON.stringify(nextLocalDevice) !== JSON.stringify(device)) {
439
- setDevice(nextLocalDevice);
440
- localStorage.setItem('device', JSON.stringify(nextLocalDevice));
441
- deviceActions.setItem(nextLocalDevice);
442
- }
443
- return;
444
- }
445
-
446
- const savedDevice = await deviceActions.save(nextDevice);
447
- if (cancelled || !savedDevice) {
448
- return;
449
- }
450
-
451
- const nextLocalDevice = {
452
- ...device,
453
- entityId: savedDevice?.id || device?.entityId || null,
454
- entityIri:
455
- savedDevice?.['@id'] ||
456
- device?.entityIri ||
457
- (savedDevice?.id ? `/devices/${savedDevice.id}` : null),
458
- alias: savedDevice.alias || nextDevice.alias,
459
- type: device?.type || runtimeDeviceType,
460
- metadata: {
461
- ...(savedDevice?.metadata || {}),
462
- ...(nextDevice.metadata || {}),
463
- },
464
- };
465
-
466
- if (JSON.stringify(nextLocalDevice) !== JSON.stringify(device)) {
467
- setDevice(nextLocalDevice);
468
- localStorage.setItem('device', JSON.stringify(nextLocalDevice));
469
- deviceActions.setItem(nextLocalDevice);
470
- }
471
- };
472
-
473
- syncDeviceRegistration().catch(() => {});
474
-
475
- return () => {
476
- cancelled = true;
477
- };
411
+
412
+ if (cancelled) {
413
+ return;
414
+ }
415
+
416
+ const existingDevice =
417
+ Array.isArray(items) && items.length > 0 ? items[0] : null;
418
+ const nextDevice = buildDeviceRegistrationPayload({
419
+ deviceInfo: device,
420
+ appType: app_type,
421
+ existingDevice,
422
+ });
423
+
424
+ if (!hasDeviceRecordChanges({existingDevice, nextDevice})) {
425
+ const nextLocalDevice = {
426
+ ...device,
427
+ entityId: existingDevice?.id || device?.entityId || null,
428
+ entityIri:
429
+ existingDevice?.['@id'] ||
430
+ device?.entityIri ||
431
+ (existingDevice?.id ? `/devices/${existingDevice.id}` : null),
432
+ alias: existingDevice?.alias || nextDevice.alias,
433
+ type: device?.type || runtimeDeviceType,
434
+ metadata: {
435
+ ...(existingDevice?.metadata || {}),
436
+ ...(nextDevice.metadata || {}),
437
+ },
438
+ };
439
+
440
+ if (JSON.stringify(nextLocalDevice) !== JSON.stringify(device)) {
441
+ setDevice(nextLocalDevice);
442
+ localStorage.setItem('device', JSON.stringify(nextLocalDevice));
443
+ deviceActions.setItem(nextLocalDevice);
444
+ }
445
+ return;
446
+ }
447
+
448
+ const savedDevice = await deviceActions.save(nextDevice);
449
+ if (cancelled || !savedDevice) {
450
+ return;
451
+ }
452
+
453
+ const nextLocalDevice = {
454
+ ...device,
455
+ entityId: savedDevice?.id || device?.entityId || null,
456
+ entityIri:
457
+ savedDevice?.['@id'] ||
458
+ device?.entityIri ||
459
+ (savedDevice?.id ? `/devices/${savedDevice.id}` : null),
460
+ alias: savedDevice.alias || nextDevice.alias,
461
+ type: device?.type || runtimeDeviceType,
462
+ metadata: {
463
+ ...(savedDevice?.metadata || {}),
464
+ ...(nextDevice.metadata || {}),
465
+ },
466
+ };
467
+
468
+ if (JSON.stringify(nextLocalDevice) !== JSON.stringify(device)) {
469
+ setDevice(nextLocalDevice);
470
+ localStorage.setItem('device', JSON.stringify(nextLocalDevice));
471
+ deviceActions.setItem(nextLocalDevice);
472
+ }
473
+ };
474
+
475
+ syncDeviceRegistration().catch(() => {});
476
+
477
+ return () => {
478
+ cancelled = true;
479
+ };
478
480
  }, [
479
481
  device?.appVersion,
480
482
  device?.batteryLevel,
481
483
  device?.buildNumber,
482
484
  device?.deviceType,
483
- device?.id,
484
- device?.metadata,
485
+ device?.id,
486
+ device?.metadata,
485
487
  device?.manufacturer,
486
488
  device?.model,
487
489
  device?.systemVersion,
488
490
  deviceConfigFetched,
489
491
  deviceConfigPeopleIri,
490
492
  isLogged,
493
+ currentCompany,
494
+ mainCompany,
491
495
  sessionChecked,
492
496
  runtimeDeviceType,
497
+ user,
493
498
  ]);
494
-
499
+
495
500
  useEffect(() => {
496
501
  if (isShopClientApp || !sessionChecked || !isLogged || !currentCompany?.id) {
497
502
  return;
@@ -501,7 +506,7 @@ export const DefaultProvider = ({
501
506
  .ensureCompanyPrintersLoaded({people: currentCompany.id})
502
507
  .catch(() => {});
503
508
  }, [currentCompany?.id, isLogged, isShopClientApp, printerActions, sessionChecked]);
504
-
509
+
505
510
  useEffect(() => {
506
511
  const paymentConfigSource = isShopClientApp
507
512
  ? Object.keys(companyConfigs || {}).length > 0
@@ -514,6 +519,10 @@ export const DefaultProvider = ({
514
519
  return;
515
520
  }
516
521
 
522
+ if (!isShopClientApp && !mainConfigsDiscovered) {
523
+ return;
524
+ }
525
+
517
526
  let isMounted = true;
518
527
 
519
528
  api
@@ -534,16 +543,13 @@ export const DefaultProvider = ({
534
543
  : Array.isArray(response)
535
544
  ? response
536
545
  : [];
537
- const allowedPaymentTypeIds = resolveDevicePaymentTypeIds(
538
- paymentConfigSource,
539
- walletPaymentTypes,
540
- );
541
-
542
546
  paymentTypeActions.setItems(
543
- filterWalletPaymentTypesByAllowedIds(
547
+ selectPosWalletPaymentTypes({
544
548
  walletPaymentTypes,
545
- allowedPaymentTypeIds,
546
- ),
549
+ deviceConfigs: paymentConfigSource,
550
+ companyConfigs,
551
+ gateway: getPaymentGateway(device_config || paymentConfigSource),
552
+ }),
547
553
  );
548
554
  })
549
555
  .catch(() => {
@@ -563,42 +569,42 @@ export const DefaultProvider = ({
563
569
  isShopClientApp,
564
570
  paymentTypeActions,
565
571
  ]);
566
-
567
- useEffect(() => {
568
- if (
569
- device &&
570
- device.id &&
571
- isLogged &&
572
- currentCompany &&
573
- Object.entries(currentCompany).length > 0
574
- ) {
575
- setDeviceConfigFetched(false);
576
- setDeviceRuntimeConfigSynced(false);
577
- deviceConfigsActions
578
- .getItems({
579
- 'device.device': device.id,
580
- people: '/people/' + currentCompany.id,
581
- type: runtimeDeviceType,
582
- })
583
- .then(data => {
584
- if (Array.isArray(data) && data.length > 0) {
585
- const nextItem = {
586
- ...data[0],
587
- configs: parseConfigsObject(data[0]?.configs),
588
- };
589
- deviceConfigsActions.setItem(nextItem);
590
- return;
591
- }
592
-
593
- deviceConfigsActions.setItem({});
594
- })
595
- .catch(() => {})
596
- .finally(() => {
597
- setDeviceConfigFetched(true);
598
- });
599
- }
600
- }, [currentCompany?.id, isLogged, device?.id, runtimeDeviceType]);
601
-
572
+
573
+ useEffect(() => {
574
+ if (
575
+ device &&
576
+ device.id &&
577
+ isLogged &&
578
+ currentCompany &&
579
+ Object.entries(currentCompany).length > 0
580
+ ) {
581
+ setDeviceConfigFetched(false);
582
+ setDeviceRuntimeConfigSynced(false);
583
+ deviceConfigsActions
584
+ .getItems({
585
+ 'device.device': device.id,
586
+ people: '/people/' + currentCompany.id,
587
+ type: runtimeDeviceType,
588
+ })
589
+ .then(data => {
590
+ if (Array.isArray(data) && data.length > 0) {
591
+ const nextItem = {
592
+ ...data[0],
593
+ configs: parseConfigsObject(data[0]?.configs),
594
+ };
595
+ deviceConfigsActions.setItem(nextItem);
596
+ return;
597
+ }
598
+
599
+ deviceConfigsActions.setItem({});
600
+ })
601
+ .catch(() => {})
602
+ .finally(() => {
603
+ setDeviceConfigFetched(true);
604
+ });
605
+ }
606
+ }, [currentCompany?.id, isLogged, device?.id, runtimeDeviceType]);
607
+
602
608
  useEffect(() => {
603
609
  if (
604
610
  !deviceConfigFetched ||
@@ -609,91 +615,91 @@ export const DefaultProvider = ({
609
615
  ) {
610
616
  return;
611
617
  }
612
-
613
- const {nextConfigs, needsUpdate} = buildProviderManagedDeviceConfigs({
614
- configs: device_config?.configs,
615
- appVersion,
616
- deviceInfo: device,
617
- });
618
-
619
- if (!needsUpdate) {
620
- setDeviceRuntimeConfigSynced(true);
621
- return;
622
- }
623
-
624
- // Non-admin users cannot mutate device context/financial policy (API 403).
625
- // Skip bootstrap write; keep local runtime configs in memory only.
626
- if (!isTenantAdministrativeAuthority(currentCompany)) {
627
- setDeviceRuntimeConfigSynced(true);
628
- return;
629
- }
630
-
631
- deviceConfigsActions
632
- .addDeviceConfigs({
633
- device: device.id,
634
- configs: JSON.stringify(nextConfigs),
635
- people: deviceConfigPeopleIri,
636
- type: runtimeDeviceType,
637
- })
638
- .catch(() => {})
639
- .finally(() => {
640
- setDeviceRuntimeConfigSynced(true);
641
- });
642
- }, [
643
- appVersion,
644
- currentCompany?.id,
618
+
619
+ const isNewPdvConfig =
620
+ runtimeDeviceType === 'PDV' &&
621
+ !device_config?.id &&
622
+ !device_config?.['@id'];
623
+ const buildDeviceConfigs = isNewPdvConfig
624
+ ? buildDefaultDeviceConfigs
625
+ : buildProviderManagedDeviceConfigs;
626
+ const {nextConfigs, needsUpdate} = buildDeviceConfigs({
627
+ configs: device_config?.configs,
628
+ appVersion,
629
+ deviceInfo: device,
630
+ });
631
+
632
+ if (!needsUpdate) {
633
+ setDeviceRuntimeConfigSynced(true);
634
+ return;
635
+ }
636
+
637
+ // app-community#821: never auto-persist device_config from global bootstrap.
638
+ // Provider-managed fields stay in memory only; API mutations only from
639
+ // explicit device settings UI (avoids 403 on order-history / PDV|MANAGER).
640
+ const currentItem = device_config || {};
641
+ deviceConfigsActions.setItem({
642
+ ...currentItem,
643
+ configs: nextConfigs,
644
+ device: currentItem.device || device.id,
645
+ people: currentItem.people || deviceConfigPeopleIri,
646
+ type: currentItem.type || runtimeDeviceType,
647
+ });
648
+ setDeviceRuntimeConfigSynced(true);
649
+ }, [
650
+ appVersion,
645
651
  device?.id,
646
652
  device?.manufacturer,
647
653
  device?.isEmulator,
648
654
  deviceConfigFetched,
649
655
  deviceConfigPeopleIri,
650
656
  deviceRuntimeConfigSynced,
651
- currentCompany,
652
657
  deviceConfigsActions,
653
- device_config?.configs,
658
+ device_config,
654
659
  isLogged,
655
660
  runtimeDeviceType,
656
- ]);
657
-
658
- useEffect(() => {
659
- if (!isLogged || !currentCompany?.id || !device?.id) {
660
- return;
661
- }
662
-
663
- setMainConfigsDiscovered(false);
664
- }, [isLogged, currentCompany?.id, device?.id]);
665
-
666
- useEffect(() => {
667
- if (
668
- !isLogged ||
669
- !currentCompany?.id ||
670
- !device?.id ||
671
- mainConfigsDiscovered
672
- ) {
673
- return;
674
- }
675
-
676
- configActions
677
- .discoveryMainConfigs({
678
- people: '/people/' + currentCompany.id,
679
- })
680
- .catch(() => {})
681
- .finally(() => {
682
- setMainConfigsDiscovered(true);
683
- });
684
- }, [configActions, currentCompany?.id, device?.id, isLogged, mainConfigsDiscovered]);
685
-
686
- useEffect(() => {
687
- if (
688
- isLogged &&
689
- currentCompany &&
690
- Object.entries(currentCompany).length > 0 &&
691
- !mainConfigsDiscovered
692
- ) {
693
- configActions.setItems(currentCompany.configs);
694
- }
695
- }, [currentCompany, isLogged, mainConfigsDiscovered]);
696
-
661
+ user,
662
+ ]);
663
+
664
+ useEffect(() => {
665
+ if (!isLogged || !currentCompany?.id || !device?.id) {
666
+ return;
667
+ }
668
+
669
+ setMainConfigsDiscovered(false);
670
+ }, [isLogged, currentCompany?.id, device?.id]);
671
+
672
+ useEffect(() => {
673
+ if (
674
+ !isLogged ||
675
+ !currentCompany?.id ||
676
+ !device?.id ||
677
+ mainConfigsDiscovered
678
+ ) {
679
+ return;
680
+ }
681
+
682
+ configActions
683
+ .discoveryMainConfigs({
684
+ people: '/people/' + currentCompany.id,
685
+ })
686
+ .catch(() => {})
687
+ .finally(() => {
688
+ setMainConfigsDiscovered(true);
689
+ });
690
+ }, [configActions, currentCompany?.id, device?.id, isLogged, mainConfigsDiscovered]);
691
+
692
+ useEffect(() => {
693
+ if (
694
+ isLogged &&
695
+ currentCompany &&
696
+ Object.entries(currentCompany).length > 0 &&
697
+ !mainConfigsDiscovered
698
+ ) {
699
+ configActions.setItems(currentCompany.configs);
700
+ }
701
+ }, [currentCompany, isLogged, mainConfigsDiscovered]);
702
+
697
703
  useEffect(() => {
698
704
  if (!currentRouteName || isPublicRouteActive) {
699
705
  return;
@@ -716,7 +722,7 @@ export const DefaultProvider = ({
716
722
  ) {
717
723
  global.t.companies = companies;
718
724
  global.t.currentCompany = currentCompany;
719
- global.t.defaultCompany = defaultCompany;
725
+ global.t.mainCompany = mainCompany;
720
726
  setActiveTranslateBootstrapKey(expectedTranslateBootstrapKey);
721
727
  setTranslateReady(true);
722
728
 
@@ -739,7 +745,7 @@ export const DefaultProvider = ({
739
745
  translateBootstrapKeyRef.current = expectedTranslateBootstrapKey;
740
746
  global.t = new Translate(
741
747
  companies,
742
- defaultCompany,
748
+ mainCompany,
743
749
  currentCompany,
744
750
  Object.keys(stores),
745
751
  translateStore,
@@ -752,7 +758,7 @@ export const DefaultProvider = ({
752
758
  configuredTranslationLanguage,
753
759
  currentCompany,
754
760
  currentRouteName,
755
- defaultCompany,
761
+ mainCompany,
756
762
  deviceConfigFetched,
757
763
  expectedTranslateBootstrapKey,
758
764
  hasCurrentCompany,
@@ -760,8 +766,8 @@ export const DefaultProvider = ({
760
766
  isPublicRouteActive,
761
767
  translateStore,
762
768
  ]);
763
-
764
-
769
+
770
+
765
771
  useEffect(() => {
766
772
  if (
767
773
  !isLogged ||
@@ -777,8 +783,8 @@ export const DefaultProvider = ({
777
783
  isTranslateBootstrapReady,
778
784
  onBootstrapReady,
779
785
  ]);
780
-
781
-
786
+
787
+
782
788
  useEffect(() => {
783
789
  if (
784
790
  device &&
@@ -827,35 +833,35 @@ export const DefaultProvider = ({
827
833
  const cssText = await api.fetch('themes-colors.css', {
828
834
  responseType: 'text',
829
835
  });
830
- const parsedColors = parseThemeCss(cssText);
831
- setBaseThemeColors(parsedColors);
832
- actions.setColors(parsedColors);
833
- } catch {
834
- setBaseThemeColors({});
835
- }
836
- };
837
-
836
+ const parsedColors = parseThemeCss(cssText);
837
+ setBaseThemeColors(parsedColors);
838
+ actions.setColors(parsedColors);
839
+ } catch {
840
+ setBaseThemeColors({});
841
+ }
842
+ };
843
+
838
844
  if (device?.id) {
839
845
  fetchColors();
840
846
  }
841
- }, [actions, currentCompany?.id, defaultCompany?.id, device?.id]);
847
+ }, [actions, currentCompany?.id, mainCompany?.id, device?.id]);
842
848
 
843
849
  useEffect(() => {
844
850
  const companyThemeColors =
845
- currentCompany?.theme?.colors || defaultCompany?.theme?.colors || {};
846
- const mergedThemeColors = {
847
- ...(baseThemeColors || {}),
848
- ...(companyThemeColors || {}),
849
- };
850
-
851
- const palette = resolveThemePalette(mergedThemeColors, runtimeColors);
852
- applyPaletteToRuntimeColors(palette, runtimeColors);
853
- applyThemeCssVariables({
854
- themeColors: mergedThemeColors,
855
- palette,
856
- });
857
-
858
- actions.setColors(mergedThemeColors);
851
+ currentCompany?.theme?.colors || mainCompany?.theme?.colors || {};
852
+ const mergedThemeColors = {
853
+ ...(baseThemeColors || {}),
854
+ ...(companyThemeColors || {}),
855
+ };
856
+
857
+ const palette = resolveThemePalette(mergedThemeColors, runtimeColors);
858
+ applyPaletteToRuntimeColors(palette, runtimeColors);
859
+ applyThemeCssVariables({
860
+ themeColors: mergedThemeColors,
861
+ palette,
862
+ });
863
+
864
+ actions.setColors(mergedThemeColors);
859
865
  }, [actions, baseThemeColors, currentCompany?.id, currentCompany?.theme?.colors]);
860
866
 
861
867
  if (requiresTranslateBootstrap && !isTranslateBootstrapReady) {
@@ -863,10 +869,10 @@ export const DefaultProvider = ({
863
869
  }
864
870
 
865
871
  return (
866
- <>
867
- {runtimeBridges}
868
- {device &&
869
- device.id && (
872
+ <>
873
+ {runtimeBridges}
874
+ {device &&
875
+ device.id && (
870
876
  <ThemeContext.Provider value={themeContextValue}>
871
877
  <View
872
878
  style={[
@@ -879,30 +885,30 @@ export const DefaultProvider = ({
879
885
  {!isShopClientApp && bottomNavigationCount === 0 && (
880
886
  <RuntimeInfoFooter
881
887
  appVersion={appVersion}
882
- defaultCompany={defaultCompany}
888
+ mainCompany={mainCompany}
883
889
  device={device}
884
890
  colors={colors}
885
891
  />
886
892
  )}
887
- </View>
888
- {!isShopClientApp && (
889
- <>
890
- {shouldRunForegroundRealtimeServices && (
891
- <>
892
- <WebsocketListener />
893
- <DeviceAlertSoundService />
894
- <ProductCatalogCacheService />
895
- <RemoteCheckoutService />
896
- <PrintService />
897
- </>
898
- )}
899
- </>
900
- )}
901
- </ThemeContext.Provider>
902
- )}
903
- </>
904
- );
905
- };
906
-
907
- export const useTheme = () => useContext(ThemeContext);
893
+ </View>
894
+ {!isShopClientApp && (
895
+ <>
896
+ {shouldRunForegroundRealtimeServices && (
897
+ <>
898
+ <WebsocketListener />
899
+ <DeviceAlertSoundService />
900
+ <ProductCatalogCacheService />
901
+ <RemoteCheckoutService />
902
+ <PrintService />
903
+ </>
904
+ )}
905
+ </>
906
+ )}
907
+ </ThemeContext.Provider>
908
+ )}
909
+ </>
910
+ );
911
+ };
912
+
913
+ export const useTheme = () => useContext(ThemeContext);
908
914
  // TODO(store-first): quando este arquivo for mexido, mover a leitura para stores, remover api.fetch e evitar repassar dados em objetos quando o store ja resolver isso.