Files
dashfront/src/automats/companyAutomat.js
2026-10-31 20:37:21 +01:00

1878 lines
58 KiB
JavaScript

/**
* @file companyAutomat.js
* @description Central company state container managing the active company profile
* and all company-coupled base data:
* - Company profile (`current`, tax rates, ISO language, root category ID)
* - Document number sequences (`numbers`)
* - POS terminals (`terminals`)
* - Suppliers & supplier article catalogs (`suppliers`, `articlesBySupplier`)
* - Units of measure (`units`)
* - User UI preferences (`viewerPosition`, `chatDrawerWidth`)
* - Warehouses (`warehouses`, `filteredWarehouses`, `warehouseFilter`)
* - Product properties (`properties`)
* - Customer groups (`customergroups`, `filteredCustomergroups`, `customergroupFilter`)
* - Shipping carriers (`carriers`, `filteredCarriers`, `carrierFilter`)
* - Brands (`brands`, `filteredBrands`, `brandFilter`)
* - Product attributes & values (`attributes`)
* - Payment accounts (`accounts`)
*
* Lives with the 1st-level navigation shell (`AppShell`) and triggers when
* `authAutomat.state.hasCompany` is true (`hasCompany` requires `hasAuth`).
*/
import { Automat } from 'react-automat';
import {
company as companyApi,
number as numberApi,
posterminal as posterminalApi,
printer as printerApi,
printeragent as printeragentApi,
agentprinter as agentprinterApi,
supplier as supplierApi,
unit as unitApi,
warehouse as warehouseApi,
propertyname as propertynameApi,
customergroup as customergroupApi,
carrier as carrierApi,
brand as brandApi,
attributename as attributenameApi,
attributevalue as attributevalueApi,
account as accountApi,
} from '../api';
import { requireArray, requireDefined, requireObject } from '../utils/apiShape';
import { normalizeCompany } from '../utils/companyForm';
import authAutomat from './authAutomat';
import sseAutomat from './sseAutomat';
// #region userSettings persistence helpers
const USER_SETTINGS_STORAGE_KEY = 'dash.userSettings';
const IDB_NAME = 'automat_db';
const IDB_STORE = 'states';
const IDB_USER_SETTINGS_KEY = 'userSettings';
function readInitialUserSettings() {
const defaults = { viewerPosition: 'top', chatDrawerWidth: 460 };
if (typeof window === 'undefined') return defaults;
try {
const raw = window.localStorage.getItem(USER_SETTINGS_STORAGE_KEY);
if (!raw) return defaults;
const parsed = JSON.parse(raw);
const viewerPosition =
parsed?.viewerPosition === 'top' ||
parsed?.viewerPosition === 'right' ||
parsed?.viewerPosition === 'below'
? parsed.viewerPosition
: defaults.viewerPosition;
const width = Number(parsed?.chatDrawerWidth);
const chatDrawerWidth = !Number.isNaN(width) && width >= 280 ? Math.round(width) : defaults.chatDrawerWidth;
return { viewerPosition, chatDrawerWidth };
} catch {
return defaults;
}
}
function persistUserSettings(settings) {
if (typeof window === 'undefined') return;
try {
window.localStorage.setItem(USER_SETTINGS_STORAGE_KEY, JSON.stringify(settings));
} catch {}
if (typeof indexedDB !== 'undefined') {
try {
const req = indexedDB.open(IDB_NAME, 1);
req.onupgradeneeded = () => {
const db = req.result;
if (!db.objectStoreNames.contains(IDB_STORE)) {
db.createObjectStore(IDB_STORE);
}
};
req.onsuccess = () => {
try {
const db = req.result;
const tx = db.transaction(IDB_STORE, 'readwrite');
tx.objectStore(IDB_STORE).put(settings, IDB_USER_SETTINGS_KEY);
} catch {}
};
} catch {}
}
}
function hydrateUserSettingsFromIdb(automat) {
if (typeof indexedDB === 'undefined') return;
try {
const req = indexedDB.open(IDB_NAME, 1);
req.onupgradeneeded = () => {
const db = req.result;
if (!db.objectStoreNames.contains(IDB_STORE)) {
db.createObjectStore(IDB_STORE);
}
};
req.onsuccess = () => {
try {
const db = req.result;
const tx = db.transaction(IDB_STORE, 'readonly');
const getReq = tx.objectStore(IDB_STORE).get(IDB_USER_SETTINGS_KEY);
getReq.onsuccess = () => {
const saved = getReq.result;
if (!saved || typeof saved !== 'object') return;
const patch = {};
if (
saved.viewerPosition === 'top' ||
saved.viewerPosition === 'right' ||
saved.viewerPosition === 'below'
) {
patch.viewerPosition = saved.viewerPosition;
}
const num = Number(saved.chatDrawerWidth);
if (!Number.isNaN(num) && num >= 280) {
patch.chatDrawerWidth = Math.round(num);
}
if (Object.keys(patch).length > 0) {
automat.setState(patch);
try {
window.localStorage.setItem(
USER_SETTINGS_STORAGE_KEY,
JSON.stringify({
viewerPosition: patch.viewerPosition ?? automat.state.viewerPosition,
chatDrawerWidth: patch.chatDrawerWidth ?? automat.state.chatDrawerWidth,
}),
);
} catch {}
}
};
} catch {}
};
} catch {}
}
// #endregion
// #region sorting & filtering helpers
const sortNumbers = (items) =>
[...items].sort((a, b) => String(a.type ?? '').localeCompare(String(b.type ?? ''), 'de'));
const sortUnits = (units) =>
[...units].sort((a, b) => {
const left = a.symbol ?? a.si ?? '';
const right = b.symbol ?? b.si ?? '';
return String(left).localeCompare(String(right), 'de');
});
const sortBrands = (brands) =>
[...brands].sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''), 'de'));
const sortGroups = (items) =>
[...items].sort((a, b) => (Number(a.sort) || 0) - (Number(b.sort) || 0));
const sortTerminals = (terminals) => {
if (!Array.isArray(terminals)) return [];
return [...terminals].sort((a, b) => {
const sDiff = (Number(a.sort) || 0) - (Number(b.sort) || 0);
if (sDiff !== 0) return sDiff;
return (Number(a.id) || 0) - (Number(b.id) || 0);
});
};
const sortPrinterAgents = (agents) => {
if (!Array.isArray(agents)) return [];
return [...agents].sort((a, b) => {
const sDiff = (Number(a.sort) || 0) - (Number(b.sort) || 0);
if (sDiff !== 0) return sDiff;
return (Number(a.id) || 0) - (Number(b.id) || 0);
});
};
const sortAgentPrinters = (printers) => {
if (!Array.isArray(printers)) return [];
return [...printers].sort((a, b) => {
const sDiff = (Number(a.sort) || 0) - (Number(b.sort) || 0);
if (sDiff !== 0) return sDiff;
return (Number(a.id) || 0) - (Number(b.id) || 0);
});
};
const sortAccounts = (items) =>
[...items].sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''), 'de'));
const filterByName = (items, filter) => {
const query = String(filter ?? '').trim().toLowerCase();
return query
? items.filter((item) => String(item.name ?? '').toLowerCase().includes(query))
: items;
};
function normalizeProperties(properties) {
if (!Array.isArray(properties)) return [];
return [...properties]
.map((p) => ({
...p,
exclusive: Boolean(p.exclusive),
}))
.sort((a, b) => {
const sDiff = (a.sort ?? 0) - (b.sort ?? 0);
if (sDiff !== 0) return sDiff;
return (a.id ?? 0) - (b.id ?? 0);
});
}
function sortAttributeValues(values) {
if (!Array.isArray(values)) return [];
return [...values].sort((a, b) => {
const sDiff = (a.sort ?? 0) - (b.sort ?? 0);
if (sDiff !== 0) return sDiff;
return (a.id ?? 0) - (b.id ?? 0);
});
}
function normalizeAttributes(attributes) {
if (!Array.isArray(attributes)) return [];
return [...attributes]
.map((attr) => ({
...attr,
exclusive: Boolean(attr.exclusive),
values: sortAttributeValues(attr.values),
}))
.sort((a, b) => {
const sDiff = (a.sort ?? 0) - (b.sort ?? 0);
if (sDiff !== 0) return sDiff;
return (a.id ?? 0) - (b.id ?? 0);
});
}
// #endregion
const initialUserSettings = readInitialUserSettings();
const initialState = {
// 1. Company profile
companies: [],
current: null,
status: 'idle',
error: null,
// 2. Number sequences
numbers: [],
numbersStatus: 'idle',
numbersError: null,
// 3. POS terminals
terminals: [],
terminalsCompanyId: null,
terminalsStatus: 'idle',
terminalsError: null,
// Printer agents & agent printers
printeragents: [],
printeragentsCompanyId: null,
printeragentsStatus: 'idle',
printeragentsError: null,
agentprinters: [],
agentprintersCompanyId: null,
agentprintersStatus: 'idle',
agentprintersError: null,
livePrinters: [], // LivePrinter[] from /api/printer/state or SSE printer/printerstate
// 4. Suppliers
suppliers: [],
articlesBySupplier: {},
articlesLoading: false,
suppliersStatus: 'idle',
suppliersError: null,
// 5. Units
units: [],
unitsStatus: 'idle',
unitsError: null,
// 6. User settings
viewerPosition: initialUserSettings.viewerPosition,
chatDrawerWidth: initialUserSettings.chatDrawerWidth,
// 7. Warehouses
warehouses: [],
filteredWarehouses: [],
warehouseFilter: '',
warehousesStatus: 'idle',
warehousesError: null,
// 8. Properties
properties: [],
propertiesStatus: 'idle',
propertiesError: null,
// 9. Customer groups
customergroups: [],
filteredCustomergroups: [],
customergroupFilter: '',
customergroupsStatus: 'idle',
customergroupsError: null,
// 10. Carriers
carriers: [],
filteredCarriers: [],
carrierFilter: '',
carriersStatus: 'idle',
carriersError: null,
// 11. Brands
brands: [],
filteredBrands: [],
brandFilter: '',
brandsStatus: 'idle',
brandsError: null,
// 12. Attributes & values
attributes: [],
attributesStatus: 'idle',
attributesError: null,
// 13. Payment accounts
accounts: [],
accountsStatus: 'idle',
accountsError: null,
};
let companyAutomat;
// #region in-flight deduplication promises
let inflightCompany = null;
let inflightNumbers = null;
let inflightTerminals = null;
let inflightPrinterAgents = null;
let inflightAgentPrinters = null;
let inflightLivePrinters = null;
let inflightSuppliers = null;
let inflightUnits = null;
let inflightWarehouses = null;
let inflightProperties = null;
let inflightCustomergroups = null;
let inflightCarriers = null;
let inflightBrands = null;
let inflightAttributes = null;
let inflightAccounts = null;
// #endregion
// #region internal state updaters & fetchers
const BRAND_FETCH_PAGE_SIZE = 100;
async function fetchAllBrands() {
let offset = 0;
let total = Infinity;
const collected = [];
while (offset === 0 || collected.length < total) {
const res = await brandApi.list({ offset, limit: BRAND_FETCH_PAGE_SIZE });
if (offset === 0) total = Number(res.total ?? res.brands?.length ?? 0);
const page = Array.isArray(res.brands) ? res.brands : [];
collected.push(...page);
offset += page.length;
if (page.length === 0) break;
}
return { brands: sortBrands(collected) };
}
function updateStateWithBrands(brands, extra = {}) {
const list = Array.isArray(brands) ? brands : [];
const filteredBrands = filterByName(list, companyAutomat.state.brandFilter);
companyAutomat.setState({ brands: list, filteredBrands, ...extra });
return list;
}
function updateStateWithWarehouses(warehouses, extra = {}) {
const list = Array.isArray(warehouses) ? warehouses : [];
const filteredWarehouses = filterByName(list, companyAutomat.state.warehouseFilter);
companyAutomat.setState({ warehouses: list, filteredWarehouses, ...extra });
return list;
}
function updateStateWithCustomergroups(groups, extra = {}) {
const list = sortGroups(Array.isArray(groups) ? groups : []);
const filteredCustomergroups = filterByName(list, companyAutomat.state.customergroupFilter);
companyAutomat.setState({ customergroups: list, filteredCustomergroups, ...extra });
return list;
}
function updateStateWithCarriers(carriers, extra = {}) {
const list = Array.isArray(carriers) ? carriers : [];
const filteredCarriers = filterByName(list, companyAutomat.state.carrierFilter);
companyAutomat.setState({ carriers: list, filteredCarriers, ...extra });
return list;
}
function patchTerminalCategories(terminalId, categoryIds) {
companyAutomat.setState({
terminals: companyAutomat.state.terminals.map((t) =>
t.id === terminalId ? { ...t, categoryIds } : t,
),
});
}
async function fetchSuppliersList() {
const data = await supplierApi.list();
requireObject(data, 'GET /api/supplier');
return requireArray(data.suppliers, 'suppliers');
}
async function fetchWarehousesList() {
const data = await warehouseApi.list();
requireObject(data, 'GET /api/warehouse');
return requireArray(data.warehouses, 'warehouses');
}
async function fetchCustomergroupsList() {
const data = await customergroupApi.list();
requireObject(data, 'GET /api/customergroup');
return requireArray(data.customergroups, 'customergroups');
}
async function fetchCarriersList() {
const data = await carrierApi.list();
if (Array.isArray(data)) return data;
if (data && Array.isArray(data.carriers)) return data.carriers;
return [];
}
// #endregion
const actions = {
// #region 1. Company profile & orchestration
// MARK: calls company.list from ../api/index.js
async load() {
const hasAuth = Boolean(
authAutomat.state.hasAuth ??
(authAutomat.state.status === 'authed' && authAutomat.state.user),
);
if (!hasAuth) return null;
if (inflightCompany) return inflightCompany;
companyAutomat.setState({ status: 'loading', error: null });
inflightCompany = (async () => {
try {
const data = await companyApi.list();
const companies = Array.isArray(data?.companies)
? data.companies.map((item) => normalizeCompany(item)).filter(Boolean)
: [];
const companyId = authAutomat.state.user?.selectedCompanyId;
const current =
companyId != null
? (companies.find(
(item) => item.id === companyId || String(item.id) === String(companyId),
) ?? null)
: null;
if (companyId != null && !current) {
throw new Error('Unternehmen nicht gefunden.');
}
companyAutomat.setState({ companies, current, status: 'succeeded', error: null });
return current;
} catch (err) {
companyAutomat.setState({ status: 'failed', error: err?.message ?? String(err) });
throw err;
} finally {
inflightCompany = null;
}
})();
return inflightCompany;
},
// MARK: calls company.list from ../api/index.js
async reload() {
const hasAuth = Boolean(
authAutomat.state.hasAuth ??
(authAutomat.state.status === 'authed' && authAutomat.state.user),
);
if (!hasAuth) return null;
try {
const data = await companyApi.list();
const companies = Array.isArray(data?.companies)
? data.companies.map((item) => normalizeCompany(item)).filter(Boolean)
: [];
const companyId = authAutomat.state.user?.selectedCompanyId;
const current =
companyId != null
? (companies.find(
(item) => item.id === companyId || String(item.id) === String(companyId),
) ?? companyAutomat.state.current)
: null;
companyAutomat.setState({ companies, current, status: 'succeeded', error: null });
return current;
} catch (err) {
companyAutomat.setState({ error: err?.message ?? String(err) });
throw err;
}
},
// MARK: calls company.update and company.list from ../api/index.js
async update(payload) {
const companyId = authAutomat.state.user?.selectedCompanyId;
if (!companyId) throw new Error('Kein Unternehmen ausgewählt.');
companyAutomat.setState({ status: 'loading', error: null });
try {
await companyApi.update(companyId, payload);
const data = await companyApi.list();
const companies = Array.isArray(data?.companies)
? data.companies
.map((item) =>
item.id === companyId || String(item.id) === String(companyId)
? normalizeCompany(item, payload)
: normalizeCompany(item),
)
.filter(Boolean)
: [];
const normalized =
companies.find((item) => item.id === companyId || String(item.id) === String(companyId)) ??
null;
if (!normalized) throw new Error('Unternehmen nicht gefunden.');
companyAutomat.setState({ companies, current: normalized, status: 'succeeded', error: null });
return normalized;
} catch (err) {
companyAutomat.setState({ status: 'failed', error: err?.message ?? String(err) });
throw err;
}
},
// MARK: loads all company-coupled base data in parallel
async loadAll() {
if (!authAutomat.state.hasCompany) return;
await Promise.allSettled([
actions.load(),
actions.loadNumbers(),
actions.loadTerminals(),
actions.loadPrinterAgents(),
actions.loadAgentPrinters(),
actions.loadLivePrinters(),
actions.loadSuppliers(),
actions.loadUnits(),
actions.loadWarehouses(),
actions.loadProperties(),
actions.loadCustomergroups(),
actions.loadCarriers(),
actions.loadBrands(),
actions.loadAttributes(),
actions.loadAccounts(),
]);
},
reset() {
inflightCompany = null;
inflightNumbers = null;
inflightTerminals = null;
inflightPrinterAgents = null;
inflightAgentPrinters = null;
inflightLivePrinters = null;
inflightSuppliers = null;
inflightUnits = null;
inflightWarehouses = null;
inflightProperties = null;
inflightCustomergroups = null;
inflightCarriers = null;
inflightBrands = null;
inflightAttributes = null;
inflightAccounts = null;
companyAutomat.setState({
...initialState,
viewerPosition: companyAutomat.state.viewerPosition,
chatDrawerWidth: companyAutomat.state.chatDrawerWidth,
});
},
// #endregion
// #region 2. Number sequences (number)
// MARK: calls number.list from ../api/index.js
loadNumbers() {
if (inflightNumbers) return inflightNumbers;
inflightNumbers = (async () => {
companyAutomat.setState({ numbersStatus: 'loading', numbersError: null });
try {
const data = await numberApi.list();
const numbers = sortNumbers(data.numbers ?? []);
companyAutomat.setState({ numbers, numbersStatus: 'ready', numbersError: null });
return numbers;
} catch (error) {
companyAutomat.setState({ numbersStatus: 'failed', numbersError: error });
throw error;
} finally {
inflightNumbers = null;
}
})();
return inflightNumbers;
},
async reloadNumbers() {
try {
const data = await numberApi.list();
const numbers = sortNumbers(data.numbers ?? []);
companyAutomat.setState({ numbers, numbersStatus: 'ready', numbersError: null });
return numbers;
} catch (error) {
companyAutomat.setState({ numbersError: error });
throw error;
}
},
ensureNumbersLoaded() {
if (companyAutomat.state.numbersStatus === 'ready') {
return Promise.resolve(companyAutomat.state.numbers);
}
return actions.loadNumbers();
},
replaceNumbers(items) {
const numbers = sortNumbers(Array.isArray(items) ? items : []);
companyAutomat.setState({ numbers, numbersStatus: 'ready', numbersError: null });
return numbers;
},
// #endregion
// #region 3. POS terminals (posterminal)
// MARK: calls posterminal.list from ../api/index.js
async loadTerminals() {
const companyId =
authAutomat.state.user?.selectedCompanyId ?? companyAutomat.state.current?.id;
if (!companyId) return [];
if (inflightTerminals) return inflightTerminals;
companyAutomat.setState({ terminalsStatus: 'loading', terminalsError: null });
inflightTerminals = (async () => {
try {
const res = await posterminalApi.list();
const terminals = sortTerminals(Array.isArray(res.posterminals) ? res.posterminals : []);
companyAutomat.setState({
terminals,
terminalsCompanyId: companyId,
terminalsStatus: 'succeeded',
terminalsError: null,
});
return terminals;
} catch (err) {
companyAutomat.setState({
terminalsStatus: 'failed',
terminalsError: err?.message ?? String(err),
});
throw err;
} finally {
inflightTerminals = null;
}
})();
return inflightTerminals;
},
loadPosterminals() {
return actions.loadTerminals();
},
reloadTerminals() {
return actions.loadTerminals();
},
async createTerminal(payload) {
const res = await posterminalApi.create(payload);
const terminal = res.posterminal;
companyAutomat.setState({
terminals: sortTerminals([...companyAutomat.state.terminals, terminal]),
});
return terminal;
},
createPosterminal(payload) {
return actions.createTerminal(payload);
},
async updateTerminal(terminalId, patch) {
const res = await posterminalApi.update(terminalId, patch);
const updated = res.posterminal;
companyAutomat.setState({
terminals: sortTerminals(
companyAutomat.state.terminals.map((t) =>
t.id === terminalId ? { ...t, ...updated } : t,
),
),
});
return updated;
},
updatePosterminal(terminalId, patch) {
return actions.updateTerminal(terminalId, patch);
},
async deleteTerminal(terminalId) {
await posterminalApi.delete(terminalId);
companyAutomat.setState({
terminals: companyAutomat.state.terminals.filter((t) => t.id !== terminalId),
});
},
deletePosterminal(terminalId) {
return actions.deleteTerminal(terminalId);
},
async enableAllTerminalCategories({ terminalId }) {
const res = await posterminalApi.enableAllCategories(terminalId);
patchTerminalCategories(terminalId, res.categoryIds);
return res;
},
enableAll(payload) {
return actions.enableAllTerminalCategories(payload);
},
async disableAllTerminalCategories({ terminalId }) {
const res = await posterminalApi.disableAllCategories(terminalId);
patchTerminalCategories(terminalId, res.categoryIds);
return res;
},
disableAll(payload) {
return actions.disableAllTerminalCategories(payload);
},
async enableTerminalCategory({ terminalId, categoryId }) {
const res = await posterminalApi.enableCategory(terminalId, categoryId);
patchTerminalCategories(terminalId, res.categoryIds);
return res;
},
enableCategory(payload) {
return actions.enableTerminalCategory(payload);
},
async disableTerminalCategory({ terminalId, categoryId }) {
const res = await posterminalApi.disableCategory(terminalId, categoryId);
patchTerminalCategories(terminalId, res.categoryIds);
return res;
},
disableCategory(payload) {
return actions.disableTerminalCategory(payload);
},
// #endregion
// #region Printer agents (printeragent)
// MARK: calls printeragent.list from ../api/index.js
async loadPrinterAgents() {
const companyId =
authAutomat.state.user?.selectedCompanyId ?? companyAutomat.state.current?.id;
if (!companyId) return [];
if (inflightPrinterAgents) return inflightPrinterAgents;
companyAutomat.setState({ printeragentsStatus: 'loading', printeragentsError: null });
inflightPrinterAgents = (async () => {
try {
const res = await printeragentApi.list();
const printeragents = sortPrinterAgents(Array.isArray(res.printeragents) ? res.printeragents : []);
companyAutomat.setState({
printeragents,
printeragentsCompanyId: companyId,
printeragentsStatus: 'succeeded',
printeragentsError: null,
});
return printeragents;
} catch (err) {
companyAutomat.setState({
printeragentsStatus: 'failed',
printeragentsError: err?.message ?? String(err),
});
throw err;
} finally {
inflightPrinterAgents = null;
}
})();
return inflightPrinterAgents;
},
reloadPrinterAgents() {
return actions.loadPrinterAgents();
},
async createPrinterAgent(payload) {
const res = await printeragentApi.create(payload);
const agent = res.printeragent;
companyAutomat.setState({
printeragents: sortPrinterAgents([...companyAutomat.state.printeragents, agent]),
});
return agent;
},
async updatePrinterAgent(agentId, patch) {
const res = await printeragentApi.update(agentId, patch);
const updated = res.printeragent;
companyAutomat.setState({
printeragents: sortPrinterAgents(
companyAutomat.state.printeragents.map((a) =>
a.id === agentId ? { ...a, ...updated } : a,
),
),
});
return updated;
},
async deletePrinterAgent(agentId) {
await printeragentApi.delete(agentId);
companyAutomat.setState({
printeragents: companyAutomat.state.printeragents.filter((a) => a.id !== agentId),
agentprinters: companyAutomat.state.agentprinters.filter((p) => p.printeragentId !== agentId),
});
},
// #endregion
// #region Agent printers (agentprinter) & live telemetry
// MARK: calls agentprinter.list from ../api/index.js
async loadAgentPrinters(printeragentId) {
const companyId =
authAutomat.state.user?.selectedCompanyId ?? companyAutomat.state.current?.id;
if (!companyId) return [];
if (inflightAgentPrinters) return inflightAgentPrinters;
companyAutomat.setState({ agentprintersStatus: 'loading', agentprintersError: null });
inflightAgentPrinters = (async () => {
try {
const res = await agentprinterApi.list(printeragentId);
const agentprinters = sortAgentPrinters(Array.isArray(res.agentprinters) ? res.agentprinters : []);
companyAutomat.setState({
agentprinters,
agentprintersCompanyId: companyId,
agentprintersStatus: 'succeeded',
agentprintersError: null,
});
return agentprinters;
} catch (err) {
companyAutomat.setState({
agentprintersStatus: 'failed',
agentprintersError: err?.message ?? String(err),
});
throw err;
} finally {
inflightAgentPrinters = null;
}
})();
return inflightAgentPrinters;
},
reloadAgentPrinters(printeragentId) {
return actions.loadAgentPrinters(printeragentId);
},
async updateAgentPrinter(id, patch) {
const res = await agentprinterApi.update(id, patch);
const updated = res.agentprinter;
companyAutomat.setState({
agentprinters: sortAgentPrinters(
companyAutomat.state.agentprinters.map((p) =>
p.id === id ? { ...p, ...updated } : p,
),
),
});
return updated;
},
async reorderAgentPrinters({ printeragentId, agentprinterIds }) {
await agentprinterApi.order({ printeragentId, agentprinterIds });
const idOrder = new Map(agentprinterIds.map((id, index) => [id, index]));
companyAutomat.setState({
agentprinters: sortAgentPrinters(
companyAutomat.state.agentprinters.map((p) =>
idOrder.has(p.id) ? { ...p, sort: idOrder.get(p.id) } : p,
),
),
});
},
async deleteAgentPrinter(id) {
await agentprinterApi.delete(id);
companyAutomat.setState({
agentprinters: companyAutomat.state.agentprinters.filter((p) => p.id !== id),
});
},
async loadLivePrinters() {
const companyId =
authAutomat.state.user?.selectedCompanyId ?? companyAutomat.state.current?.id;
if (!companyId) return [];
if (inflightLivePrinters) return inflightLivePrinters;
inflightLivePrinters = (async () => {
try {
const res = await printerApi.state();
const livePrinters = Array.isArray(res.printers) ? res.printers : [];
companyAutomat.setState({ livePrinters });
return livePrinters;
} catch (err) {
return [];
} finally {
inflightLivePrinters = null;
}
})();
return inflightLivePrinters;
},
setLivePrinters(printers) {
companyAutomat.setState({
livePrinters: Array.isArray(printers) ? printers : [],
});
},
// #endregion
// #region 4. Suppliers (supplier)
// MARK: calls supplier.list from ../api/index.js
loadSuppliers() {
if (inflightSuppliers) return inflightSuppliers;
inflightSuppliers = (async () => {
companyAutomat.setState({ suppliersStatus: 'loading', suppliersError: null });
try {
const suppliers = await fetchSuppliersList();
companyAutomat.setState({ suppliers, suppliersStatus: 'ready', suppliersError: null });
void actions.loadAllSupplierArticles().catch(() => {});
return suppliers;
} catch (error) {
companyAutomat.setState({ suppliersStatus: 'failed', suppliersError: error });
throw error;
} finally {
inflightSuppliers = null;
}
})();
return inflightSuppliers;
},
async reloadSuppliers() {
if (inflightSuppliers) return inflightSuppliers;
inflightSuppliers = (async () => {
try {
const suppliers = await fetchSuppliersList();
companyAutomat.setState({ suppliers, suppliersStatus: 'ready', suppliersError: null });
void actions.loadAllSupplierArticles().catch(() => {});
return suppliers;
} catch (error) {
companyAutomat.setState({ suppliersError: error });
throw error;
} finally {
inflightSuppliers = null;
}
})();
return inflightSuppliers;
},
async loadArticlesForSupplier(supplierId) {
const sId = String(supplierId);
if (companyAutomat.state.articlesBySupplier?.[sId]) {
return companyAutomat.state.articlesBySupplier[sId];
}
const res = await supplierApi.getArticles(supplierId);
const articles = Array.isArray(res?.articles) ? res.articles : [];
companyAutomat.setState({
articlesBySupplier: {
...(companyAutomat.state.articlesBySupplier || {}),
[sId]: articles,
},
});
return articles;
},
async loadAllSupplierArticles() {
const suppliers = companyAutomat.state.suppliers;
if (!suppliers.length) return {};
companyAutomat.setState({ articlesLoading: true });
try {
const results = await Promise.allSettled(
suppliers.map(async (s) => {
const res = await supplierApi.getArticles(s.id);
return { id: String(s.id), articles: Array.isArray(res?.articles) ? res.articles : [] };
}),
);
const nextMap = { ...(companyAutomat.state.articlesBySupplier || {}) };
for (const r of results) {
if (r.status === 'fulfilled') {
nextMap[r.value.id] = r.value.articles;
}
}
companyAutomat.setState({ articlesBySupplier: nextMap, articlesLoading: false });
return nextMap;
} catch {
companyAutomat.setState({ articlesLoading: false });
return companyAutomat.state.articlesBySupplier || {};
}
},
syncArticleSuppliers(sku, suppliers) {
const artSku = String(sku).trim().toLowerCase();
const assignedSupplierIds = new Set(
(suppliers || []).map((s) => String(s.entityId ?? s.supplierId ?? s.id))
);
const prevMap = companyAutomat.state.articlesBySupplier || {};
let changed = false;
const nextMap = { ...prevMap };
for (const sId of assignedSupplierIds) {
const currentList = nextMap[sId] ? [...nextMap[sId]] : [];
const hasSku = currentList.some(
(item) => String(item.article?.sku ?? item.sku ?? '').toLowerCase() === artSku
);
if (!hasSku) {
currentList.push({ sku, article: { sku } });
nextMap[sId] = currentList;
changed = true;
}
}
for (const [sId, list] of Object.entries(nextMap)) {
if (!assignedSupplierIds.has(sId) && Array.isArray(list)) {
const filtered = list.filter(
(item) => String(item.article?.sku ?? item.sku ?? '').toLowerCase() !== artSku
);
if (filtered.length !== list.length) {
nextMap[sId] = filtered;
changed = true;
}
}
}
if (changed) {
companyAutomat.setState({ articlesBySupplier: nextMap });
}
},
async createSupplier(body) {
const data = await supplierApi.create(body);
requireObject(data, 'POST /api/supplier');
requireObject(data.supplier, 'supplier');
requireDefined(data.supplier.id, 'supplier.id');
await actions.reloadSuppliers();
return data.supplier;
},
async updateSupplier(id, body) {
const data = await supplierApi.update(id, body);
requireObject(data, 'PATCH /api/supplier/:id');
requireObject(data.supplier, 'supplier');
requireDefined(data.supplier.id, 'supplier.id');
await actions.reloadSuppliers();
return data.supplier;
},
async removeSupplier(id) {
await supplierApi.delete(id);
await actions.reloadSuppliers();
},
setSuppliers(suppliers) {
companyAutomat.setState({
suppliers: Array.isArray(suppliers) ? suppliers : [],
suppliersStatus: 'ready',
suppliersError: null,
});
},
// #endregion
// #region 5. Units (unit)
// MARK: calls unit.list from ../api/index.js
loadUnits() {
if (inflightUnits) return inflightUnits;
companyAutomat.setState({ unitsStatus: 'loading', unitsError: null });
inflightUnits = (async () => {
try {
const res = await unitApi.list();
const units = sortUnits(Array.isArray(res.units) ? res.units : []);
companyAutomat.setState({ units, unitsStatus: 'succeeded', unitsError: null });
return units;
} catch (err) {
companyAutomat.setState({
unitsStatus: 'failed',
unitsError: err?.message ?? String(err),
});
throw err;
} finally {
inflightUnits = null;
}
})();
return inflightUnits;
},
async upsertUnit(body) {
const res = await unitApi.upsert(body);
const row = res.unit;
const units = sortUnits([
...companyAutomat.state.units.filter((u) => u.si !== row.si),
row,
]);
companyAutomat.setState({ units });
return units;
},
async deleteUnit(si) {
await unitApi.delete(si);
companyAutomat.setState({
units: companyAutomat.state.units.filter((u) => u.si !== si),
});
},
// #endregion
// #region 6. User settings (userSetting)
setViewerPosition(position) {
if (position !== 'top' && position !== 'right' && position !== 'below') {
return;
}
companyAutomat.setState({ viewerPosition: position });
persistUserSettings({
viewerPosition: position,
chatDrawerWidth: companyAutomat.state.chatDrawerWidth,
});
},
setChatDrawerWidth(width) {
const num = Number(width);
if (Number.isNaN(num) || num < 280) {
return;
}
const rounded = Math.round(num);
companyAutomat.setState({ chatDrawerWidth: rounded });
persistUserSettings({
viewerPosition: companyAutomat.state.viewerPosition,
chatDrawerWidth: rounded,
});
},
// #endregion
// #region 7. Warehouses (warehouse)
// MARK: calls warehouse.list from ../api/index.js
filterWarehouses(query) {
const warehouseFilter = String(query ?? '');
companyAutomat.setState({
warehouseFilter,
filteredWarehouses: filterByName(companyAutomat.state.warehouses, warehouseFilter),
});
},
loadWarehouses() {
if (inflightWarehouses) return inflightWarehouses;
inflightWarehouses = (async () => {
companyAutomat.setState({ warehousesStatus: 'loading', warehousesError: null });
try {
const warehouses = await fetchWarehousesList();
updateStateWithWarehouses(warehouses, {
warehousesStatus: 'ready',
warehousesError: null,
});
return warehouses;
} catch (error) {
companyAutomat.setState({ warehousesStatus: 'failed', warehousesError: error });
throw error;
} finally {
inflightWarehouses = null;
}
})();
return inflightWarehouses;
},
async reloadWarehouses() {
try {
const warehouses = await fetchWarehousesList();
updateStateWithWarehouses(warehouses, {
warehousesStatus: 'ready',
warehousesError: null,
});
return warehouses;
} catch (error) {
companyAutomat.setState({ warehousesError: error });
throw error;
}
},
async createWarehouse(body) {
const data = await warehouseApi.create(body);
requireObject(data, 'POST /api/warehouse');
requireObject(data.warehouse, 'warehouse');
requireDefined(data.warehouse.id, 'warehouse.id');
await actions.reloadWarehouses();
return data.warehouse;
},
async updateWarehouse(id, body) {
const data = await warehouseApi.update(id, body);
requireObject(data, 'PATCH /api/warehouse/:id');
requireObject(data.warehouse, 'warehouse');
requireDefined(data.warehouse.id, 'warehouse.id');
await actions.reloadWarehouses();
return data.warehouse;
},
renameWarehouse(id, body) {
return actions.updateWarehouse(id, body);
},
async removeWarehouse(id) {
await warehouseApi.delete(id);
await actions.reloadWarehouses();
},
async reorderWarehouses(warehouseIds) {
const byId = new Map(companyAutomat.state.warehouses.map((w) => [w.id, w]));
const reordered = warehouseIds.map((id) => byId.get(id)).filter(Boolean);
if (reordered.length === companyAutomat.state.warehouses.length) {
updateStateWithWarehouses(reordered);
}
await warehouseApi.order({ warehouseIds });
await actions.reloadWarehouses();
},
setWarehouses(warehouses) {
updateStateWithWarehouses(warehouses, {
warehousesStatus: 'ready',
warehousesError: null,
});
},
// #endregion
// #region 8. Properties (property)
// MARK: calls propertyname.list from ../api/index.js
loadProperties() {
if (inflightProperties) return inflightProperties;
companyAutomat.setState({ propertiesStatus: 'loading', propertiesError: null });
inflightProperties = (async () => {
try {
const res = await propertynameApi.list();
const properties = normalizeProperties(res.propertynames);
companyAutomat.setState({
properties,
propertiesStatus: 'succeeded',
propertiesError: null,
});
return properties;
} catch (err) {
companyAutomat.setState({
propertiesStatus: 'failed',
propertiesError: err?.message ?? String(err),
});
throw err;
} finally {
inflightProperties = null;
}
})();
return inflightProperties;
},
async createProperty(body) {
const res = await propertynameApi.create(body);
await actions.loadProperties();
return res.propertyname;
},
async updateProperty(id, body) {
const res = await propertynameApi.update(id, body);
await actions.loadProperties();
return res.propertyname;
},
async deleteProperty(id) {
await propertynameApi.delete(id);
await actions.loadProperties();
},
async deleteProperties(ids) {
for (const id of ids) {
await propertynameApi.delete(id);
}
await actions.loadProperties();
},
async reorderProperties(propertynameIds) {
await propertynameApi.order(propertynameIds);
await actions.loadProperties();
},
// #endregion
// #region 9. Customer groups (customergroup)
// MARK: calls customergroup.list from ../api/index.js
filterCustomergroups(query) {
const customergroupFilter = String(query ?? '');
companyAutomat.setState({
customergroupFilter,
filteredCustomergroups: filterByName(
companyAutomat.state.customergroups,
customergroupFilter,
),
});
},
loadCustomergroups() {
if (inflightCustomergroups) return inflightCustomergroups;
inflightCustomergroups = (async () => {
companyAutomat.setState({ customergroupsStatus: 'loading', customergroupsError: null });
try {
const groups = await fetchCustomergroupsList();
updateStateWithCustomergroups(groups, {
customergroupsStatus: 'ready',
customergroupsError: null,
});
return companyAutomat.state.customergroups;
} catch (error) {
companyAutomat.setState({
customergroupsStatus: 'failed',
customergroupsError: error,
});
throw error;
} finally {
inflightCustomergroups = null;
}
})();
return inflightCustomergroups;
},
async reloadCustomergroups() {
try {
const groups = await fetchCustomergroupsList();
updateStateWithCustomergroups(groups, {
customergroupsStatus: 'ready',
customergroupsError: null,
});
return companyAutomat.state.customergroups;
} catch (error) {
companyAutomat.setState({ customergroupsError: error });
throw error;
}
},
replaceCustomergroups(items) {
return updateStateWithCustomergroups(items, {
customergroupsStatus: 'ready',
customergroupsError: null,
});
},
ensureCustomergroupsLoaded() {
if (companyAutomat.state.customergroupsStatus === 'ready') {
return Promise.resolve(companyAutomat.state.customergroups);
}
return actions.loadCustomergroups();
},
async createCustomergroup(body) {
const data = await customergroupApi.create(body);
requireObject(data, 'POST /api/customergroup');
requireObject(data.customergroup, 'customergroup');
requireDefined(data.customergroup.id, 'customergroup.id');
await actions.reloadCustomergroups();
return data.customergroup;
},
async updateCustomergroup(id, body) {
const data = await customergroupApi.update(id, body);
requireObject(data, 'PATCH /api/customergroup/:id');
requireObject(data.customergroup, 'customergroup');
requireDefined(data.customergroup.id, 'customergroup.id');
await actions.reloadCustomergroups();
return data.customergroup;
},
renameCustomergroup(id, body) {
return actions.updateCustomergroup(id, body);
},
async removeCustomergroup(id) {
await customergroupApi.delete(id);
await actions.reloadCustomergroups();
},
async reorderCustomergroups(customergroupIds) {
const data = await customergroupApi.order({ customergroupIds });
requireObject(data, 'PUT /api/customergroup/order');
await actions.reloadCustomergroups();
},
// #endregion
// #region 10. Carriers (carrier)
// MARK: calls carrier.list from ../api/index.js
filterCarriers(query) {
const carrierFilter = String(query ?? '');
companyAutomat.setState({
carrierFilter,
filteredCarriers: filterByName(companyAutomat.state.carriers, carrierFilter),
});
},
loadCarriers() {
if (inflightCarriers) return inflightCarriers;
inflightCarriers = (async () => {
companyAutomat.setState({ carriersStatus: 'loading', carriersError: null });
try {
const carriers = await fetchCarriersList();
updateStateWithCarriers(carriers, {
carriersStatus: 'ready',
carriersError: null,
});
return carriers;
} catch (error) {
companyAutomat.setState({ carriersStatus: 'failed', carriersError: error });
throw error;
} finally {
inflightCarriers = null;
}
})();
return inflightCarriers;
},
async reloadCarriers() {
try {
const carriers = await fetchCarriersList();
updateStateWithCarriers(carriers, {
carriersStatus: 'ready',
carriersError: null,
});
return carriers;
} catch (error) {
companyAutomat.setState({ carriersError: error });
throw error;
}
},
async createCarrier(body) {
const data = await carrierApi.create(body);
const created = data?.carrier || data;
await actions.reloadCarriers();
return created;
},
async updateCarrier(id, body) {
const data = await carrierApi.update(id, body);
const updated = data?.carrier || data;
await actions.reloadCarriers();
return updated;
},
async removeCarrier(id) {
await carrierApi.delete(id);
await actions.reloadCarriers();
},
async reorderCarriers(carrierIds) {
const byId = new Map(companyAutomat.state.carriers.map((c) => [c.id, c]));
const reordered = carrierIds.map((id) => byId.get(id)).filter(Boolean);
if (reordered.length === companyAutomat.state.carriers.length) {
updateStateWithCarriers(reordered);
}
await carrierApi.order({ carrierIds });
await actions.reloadCarriers();
},
setCarriers(carriers) {
updateStateWithCarriers(carriers, {
carriersStatus: 'ready',
carriersError: null,
});
},
// #endregion
// #region 11. Brands (brand)
// MARK: calls brand.list from ../api/index.js
filterBrands(query) {
const brandFilter = String(query ?? '');
companyAutomat.setState({
brandFilter,
filteredBrands: filterByName(companyAutomat.state.brands, brandFilter),
});
},
async loadBrands(silent = false) {
if (inflightBrands) return inflightBrands;
if (!silent) {
companyAutomat.setState({ brandsStatus: 'loading', brandsError: null });
}
inflightBrands = fetchAllBrands()
.then(({ brands }) =>
updateStateWithBrands(brands, { brandsStatus: 'succeeded', brandsError: null }),
)
.catch((err) => {
const brandsError = err?.message ?? String(err);
companyAutomat.setState(
silent ? { brandsError } : { brandsStatus: 'failed', brandsError },
);
throw err;
})
.finally(() => {
inflightBrands = null;
});
return inflightBrands;
},
async createBrand(body) {
const res = await brandApi.create(body);
const row = res.brand;
updateStateWithBrands(
sortBrands([...companyAutomat.state.brands.filter((b) => b.id !== row.id), row]),
);
return row;
},
async renameBrand(id, body) {
const res = await brandApi.rename(id, body);
const updated = res.brand ?? { id: res.id, name: res.name, gsprpinfo: res.gsprpinfo };
updateStateWithBrands(
sortBrands(
companyAutomat.state.brands.map((b) => (b.id === id ? { ...b, ...updated } : b)),
),
);
return res;
},
async removeBrand(id) {
await brandApi.delete(id);
updateStateWithBrands(companyAutomat.state.brands.filter((b) => b.id !== id));
},
async removeBrands(ids) {
const idSet = new Set(ids);
for (const id of ids) {
await brandApi.delete(id);
}
updateStateWithBrands(companyAutomat.state.brands.filter((b) => !idSet.has(b.id)));
await actions.loadBrands(true);
},
async addBrandImage(id, file) {
const res = await brandApi.addImage(id, file);
await actions.loadBrands(true);
return res.brand;
},
async deleteBrandImage(id) {
const res = await brandApi.deleteImage(id);
const updated = res.brand ?? { pics: [] };
updateStateWithBrands(
companyAutomat.state.brands.map((b) =>
b.id === id ? { ...b, ...updated, pics: [] } : b,
),
);
return res.brand;
},
// #endregion
// #region 12. Attributes & values (attribute)
// MARK: calls attributename.list from ../api/index.js
loadAttributes() {
if (inflightAttributes) return inflightAttributes;
companyAutomat.setState({ attributesStatus: 'loading', attributesError: null });
inflightAttributes = (async () => {
try {
const res = await attributenameApi.list();
const attributes = normalizeAttributes(res.attributenames);
companyAutomat.setState({
attributes,
attributesStatus: 'succeeded',
attributesError: null,
});
return attributes;
} catch (err) {
companyAutomat.setState({
attributesStatus: 'failed',
attributesError: err?.message ?? String(err),
});
throw err;
} finally {
inflightAttributes = null;
}
})();
return inflightAttributes;
},
async createAttribute(body) {
const res = await attributenameApi.create(body);
await actions.loadAttributes();
return res.attributename;
},
async updateAttribute(id, body) {
const res = await attributenameApi.update(id, body);
await actions.loadAttributes();
return res.attributename;
},
async deleteAttribute(id) {
await attributenameApi.delete(id);
await actions.loadAttributes();
},
async deleteAttributes(ids) {
for (const id of ids) {
await attributenameApi.delete(id);
}
await actions.loadAttributes();
},
async reorderAttributes(attributenameIds) {
await attributenameApi.order(attributenameIds);
await actions.loadAttributes();
},
async createValue(attributeId, body) {
const res = await attributevalueApi.create(attributeId, body);
await actions.loadAttributes();
return res.attributevalue;
},
createAttributeValue(attributeId, body) {
return actions.createValue(attributeId, body);
},
async updateValue(id, body) {
const res = await attributevalueApi.update(id, body);
await actions.loadAttributes();
return res.attributevalue;
},
updateAttributeValue(id, body) {
return actions.updateValue(id, body);
},
async deleteValue(id) {
await attributevalueApi.delete(id);
await actions.loadAttributes();
},
deleteAttributeValue(id) {
return actions.deleteValue(id);
},
async deleteValues(ids) {
for (const id of ids) {
await attributevalueApi.delete(id);
}
await actions.loadAttributes();
},
deleteAttributeValues(ids) {
return actions.deleteValues(ids);
},
async reorderValues(attributeId, attributevalueIds) {
await attributevalueApi.order(attributeId, attributevalueIds);
await actions.loadAttributes();
},
reorderAttributeValues(attributeId, attributevalueIds) {
return actions.reorderValues(attributeId, attributevalueIds);
},
// #endregion
// #region 13. Payment accounts (account)
// MARK: calls account.list from ../api/index.js
loadAccounts() {
if (inflightAccounts) return inflightAccounts;
inflightAccounts = (async () => {
companyAutomat.setState({ accountsStatus: 'loading', accountsError: null });
try {
const res = await accountApi.list();
const accounts = sortAccounts(Array.isArray(res?.accounts) ? res.accounts : []);
companyAutomat.setState({
accounts,
accountsStatus: 'ready',
accountsError: null,
});
return accounts;
} catch (error) {
companyAutomat.setState({ accountsStatus: 'failed', accountsError: error });
throw error;
} finally {
inflightAccounts = null;
}
})();
return inflightAccounts;
},
async reloadAccounts() {
try {
const res = await accountApi.list();
const accounts = sortAccounts(Array.isArray(res?.accounts) ? res.accounts : []);
companyAutomat.setState({
accounts,
accountsStatus: 'ready',
accountsError: null,
});
return accounts;
} catch (error) {
companyAutomat.setState({ accountsError: error });
throw error;
}
},
ensureAccountsLoaded() {
if (companyAutomat.state.accountsStatus === 'ready') {
return Promise.resolve(companyAutomat.state.accounts);
}
return actions.loadAccounts();
},
replaceAccounts(items) {
const accounts = sortAccounts(Array.isArray(items) ? items : []);
companyAutomat.setState({ accounts, accountsStatus: 'ready', accountsError: null });
return accounts;
},
// #endregion
};
/**
* Global company state container.
* Instantiated with the 1st-level navigation shell (`AppShell`) and loads all
* company-coupled base data when `authAutomat.state.hasCompany` is true.
*
* @type {Automat<typeof initialState>}
*/
companyAutomat = new Automat(initialState, actions, { name: 'company' });
// Hydrate user layout settings from IndexedDB if present
hydrateUserSettingsFromIdb(companyAutomat);
// #region authAutomat synchronization
let lastAuthKey;
function syncWithAuth(auth) {
const hasAuth = Boolean(
auth?.hasAuth ?? (auth?.status === 'authed' && auth?.user),
);
const hasCompany = Boolean(
auth?.hasCompany ?? (hasAuth && auth?.user?.selectedCompanyId != null),
);
const companyId = hasCompany ? String(auth.user.selectedCompanyId) : null;
const authKey = hasAuth ? `authed:${companyId ?? ''}` : 'anon';
if (authKey === lastAuthKey) return;
lastAuthKey = authKey;
if (!hasAuth) {
companyAutomat.actions.reset();
} else if (companyId == null) {
companyAutomat.actions.reset();
void companyAutomat.actions.load().catch(() => {});
} else {
companyAutomat.actions.reset();
void companyAutomat.actions.loadAll();
}
}
authAutomat.subscribe(syncWithAuth);
syncWithAuth(authAutomat.state);
// #endregion
// #region sseAutomat synchronization
sseAutomat.actions.on('supplier', () => {
void companyAutomat.actions.reloadSuppliers().catch(() => {});
});
sseAutomat.actions.on('warehouse', () => {
void companyAutomat.actions.reloadWarehouses().catch(() => {});
});
sseAutomat.actions.on('brand', () => {
void companyAutomat.actions.loadBrands(true).catch(() => {});
});
sseAutomat.actions.on('unit', () => {
void companyAutomat.actions.loadUnits().catch(() => {});
});
sseAutomat.actions.on('carrier', () => {
void companyAutomat.actions.reloadCarriers().catch(() => {});
});
sseAutomat.actions.on('posterminal', () => {
void companyAutomat.actions.loadTerminals().catch(() => {});
});
sseAutomat.actions.on('printeragent', () => {
void companyAutomat.actions.loadPrinterAgents().catch(() => {});
});
sseAutomat.actions.on('agentprinter', () => {
void companyAutomat.actions.loadAgentPrinters().catch(() => {});
});
sseAutomat.actions.on('printerstate', (ev) => {
try {
const payload = typeof ev?.data === 'string' ? JSON.parse(ev.data || '{}') : (ev?.data || {});
if (Array.isArray(payload.printers)) {
companyAutomat.actions.setLivePrinters(payload.printers);
return;
}
} catch {}
void companyAutomat.actions.loadLivePrinters().catch(() => {});
});
sseAutomat.actions.on('printer', (ev) => {
try {
const payload = typeof ev?.data === 'string' ? JSON.parse(ev.data || '{}') : (ev?.data || {});
if (Array.isArray(payload.printers)) {
companyAutomat.actions.setLivePrinters(payload.printers);
}
} catch {}
});
sseAutomat.actions.on('customergroup', () => {
void companyAutomat.actions.reloadCustomergroups().catch(() => {});
});
sseAutomat.actions.on('propertyname', () => {
void companyAutomat.actions.loadProperties().catch(() => {});
});
sseAutomat.actions.on('attributename', () => {
void companyAutomat.actions.loadAttributes().catch(() => {});
});
sseAutomat.actions.on('account', () => {
void companyAutomat.actions.reloadAccounts().catch(() => {});
});
sseAutomat.actions.on('company', () => {
void companyAutomat.actions.reload().catch(() => {});
});
// #endregion
// #region selectors & exported action helpers
// Company profile selectors
export const selectCompanies = () => companyAutomat.state.companies;
export const selectCurrentCompany = () => companyAutomat.state.current;
export const selectCompanyId = () => companyAutomat.state.current?.id ?? null;
export const selectCompanyStatus = () => companyAutomat.state.status;
export const selectCompanyName = () => companyAutomat.state.current?.name;
export const selectCompanyIso639 = () => companyAutomat.state.current?.iso639;
export const selectCompanyLanguageId = () => companyAutomat.state.current?.languageId ?? null;
export const selectCompanyTaxrate = () => companyAutomat.state.current?.taxrate ?? null;
export const selectCompanyReducedTaxrate = () => companyAutomat.state.current?.reducedtaxrate ?? null;
export const selectCompanyTaxfreerate = () => companyAutomat.state.current?.taxfreerate ?? null;
export const selectCompanyRootCategoryId = () => companyAutomat.state.current?.rootCategoryId ?? null;
// Numbers selectors
export const selectNumbers = () => companyAutomat.state.numbers;
export const selectNumbersStatus = () => companyAutomat.state.numbersStatus;
// POS terminals selectors
export const selectPosterminals = () => companyAutomat.state.terminals;
export const selectPosterminalStatus = () => companyAutomat.state.terminalsStatus;
export const selectHasPosterminals = () => companyAutomat.state.terminals.length > 0;
// Printer agents selectors
export const selectPrinterAgents = () => companyAutomat.state.printeragents;
export const selectPrinterAgentStatus = () => companyAutomat.state.printeragentsStatus;
export const selectHasPrinterAgents = () => companyAutomat.state.printeragents.length > 0;
// Agent printers selectors
export const selectAgentPrinters = () => companyAutomat.state.agentprinters;
export const selectAgentPrinterStatus = () => companyAutomat.state.agentprintersStatus;
export const selectLivePrinters = () => companyAutomat.state.livePrinters;
// Suppliers selectors
export const selectSuppliers = () => companyAutomat.state.suppliers;
export const selectSuppliersStatus = () => companyAutomat.state.suppliersStatus;
// Units selectors
export const selectUnits = () => companyAutomat.state.units;
export const selectUnitsStatus = () => companyAutomat.state.unitsStatus;
// User settings helpers & selectors
export const setViewerPosition = (position) => companyAutomat.actions.setViewerPosition(position);
export const setChatDrawerWidth = (width) => companyAutomat.actions.setChatDrawerWidth(width);
export const selectChatDrawerWidth = () => companyAutomat.state.chatDrawerWidth || 460;
// Warehouses selectors
export const selectWarehouses = () => companyAutomat.state.warehouses;
export const selectWarehousesStatus = () => companyAutomat.state.warehousesStatus;
// Properties selectors
export const selectProperties = () => companyAutomat.state.properties;
export const selectPropertyStatus = () => companyAutomat.state.propertiesStatus;
// Customer groups selectors
export const selectCustomergroups = () => companyAutomat.state.customergroups;
export const selectCustomergroupsStatus = () => companyAutomat.state.customergroupsStatus;
// Carriers selectors
export const selectCarriers = () => companyAutomat.state.carriers;
export const selectCarriersStatus = () => companyAutomat.state.carriersStatus;
// Brands selectors
export const selectBrands = () => companyAutomat.state.brands;
export const selectBrandsStatus = () => companyAutomat.state.brandsStatus;
// Attributes selectors
export const selectAttributes = () => companyAutomat.state.attributes;
export const selectAttributeStatus = () => companyAutomat.state.attributesStatus;
// Accounts selectors
export const selectAccounts = () => companyAutomat.state.accounts;
export const selectAccountsStatus = () => companyAutomat.state.accountsStatus;
// #endregion
export default companyAutomat;