@lambda-development/erp-core 1.1.4 → 1.1.6

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.
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { jsx as t, jsxs as r, Fragment as ke } from "react/jsx-runtime";
2
- import { useState as w, useEffect as ce, useCallback as Ve, useContext as Sn, createContext as An, useRef as pe, useMemo as fe, useLayoutEffect as Mn, StrictMode as Lr } from "react";
2
+ import { useState as w, useEffect as ce, useCallback as Ve, useContext as Sn, createContext as An, useRef as pe, useMemo as fe, useLayoutEffect as qn, StrictMode as Lr } from "react";
3
3
  import { createRoot as Or } from "react-dom/client";
4
4
  import { useQueryClient as et, useQuery as ye, useMutation as xe, QueryClientProvider as Fr, QueryClient as zr } from "@tanstack/react-query";
5
- import { NavLink as Zt, useLocation as Ct, useNavigate as Ue, Outlet as qr, Navigate as It, Link as Ce, useSearchParams as Et, useParams as bt, createBrowserRouter as Mr, RouterProvider as $r } from "react-router-dom";
5
+ import { NavLink as Zt, useLocation as Ct, useNavigate as Ue, Outlet as Mr, Navigate as It, Link as Ce, useSearchParams as Et, useParams as bt, createBrowserRouter as qr, RouterProvider as $r } from "react-router-dom";
6
6
  import { useTranslation as ie, initReactI18next as Vr } from "react-i18next";
7
- import { BookOpen as Ur, ShoppingCart as Ta, CreditCard as Kr, FileText as _n, Package as Ra, BarChart3 as jr, CalendarRange as Wr, Database as Gr, Settings as Hr, MessageCircle as Ba, ChevronDown as St, ChevronRight as Lt, Plus as Ia, Trash2 as Ea, LineChart as Qr, ArrowLeft as Yr, TrendingUp as Jr, ArrowDownRight as Zr, ArrowUpRight as Xr, Wallet as ei, Receipt as $n, ChevronLeft as ti, Info as ni, Search as Vn, Filter as ai, X as ri, SlidersHorizontal as La, ChevronUp as Oa, ChevronsUpDown as Fa, Plug as za, ShieldCheck as qa, FileUp as ii, Loader2 as yn, CheckCircle2 as bn, AlertTriangle as si, RefreshCw as oi, Undo2 as li, Monitor as ci, Terminal as di, Code2 as ui, Check as Ma, Copy as mi } from "lucide-react";
7
+ import { BookOpen as Ur, ShoppingCart as Ta, CreditCard as Kr, FileText as _n, Package as Ra, BarChart3 as jr, CalendarRange as Wr, Database as Gr, Settings as Hr, MessageCircle as Ba, ChevronDown as St, ChevronRight as Lt, Plus as Ia, Trash2 as Ea, LineChart as Qr, ArrowLeft as Yr, TrendingUp as Jr, ArrowDownRight as Zr, ArrowUpRight as Xr, Wallet as ei, Receipt as $n, ChevronLeft as ti, Info as ni, Search as Vn, Filter as ai, X as ri, SlidersHorizontal as La, ChevronUp as Oa, ChevronsUpDown as Fa, Plug as za, ShieldCheck as Ma, FileUp as ii, Loader2 as yn, CheckCircle2 as bn, AlertTriangle as si, RefreshCw as oi, Undo2 as li, Monitor as ci, Terminal as di, Code2 as ui, Check as qa, Copy as mi } from "lucide-react";
8
8
  import { createColumnHelper as $a, useReactTable as Va, getCoreRowModel as Ua, flexRender as Ht } from "@tanstack/react-table";
9
9
  import { createPortal as Ka } from "react-dom";
10
10
  import { ResponsiveContainer as pi, LineChart as hi, CartesianGrid as Un, XAxis as Kn, YAxis as jn, Tooltip as rn, Legend as Wn, Line as gi, PieChart as fi, Pie as yi, Cell as bi, BarChart as vi, Bar as xi } from "recharts";
@@ -37,7 +37,7 @@ function Ga(e) {
37
37
  }
38
38
  return String(e);
39
39
  }
40
- async function q(e, n) {
40
+ async function M(e, n) {
41
41
  const a = await fetch(`${ot}${e}`, {
42
42
  headers: { "Content-Type": "application/json" },
43
43
  credentials: "include",
@@ -74,7 +74,7 @@ function Ee(e) {
74
74
  }
75
75
  const O = {
76
76
  // Generic request (used by sidebar for chat sessions)
77
- request: q,
77
+ request: M,
78
78
  // Chat attachments
79
79
  uploadChatAttachment: async (e, n) => {
80
80
  const a = new FormData();
@@ -101,59 +101,59 @@ const O = {
101
101
  const a = new FormData();
102
102
  return e.forEach((i) => a.append("files", i)), a.append("mappings", JSON.stringify(n)), Gn("/bank-statements/import", a);
103
103
  },
104
- listBankStatementImports: (e = 50) => q(`/bank-statements${Ee({ limit: e })}`),
104
+ listBankStatementImports: (e = 50) => M(`/bank-statements${Ee({ limit: e })}`),
105
105
  bankStatementSourceUrl: (e) => `${ot}/bank-statements/${encodeURIComponent(e)}/source`,
106
- listBankReconciliationTransactions: (e = "Unreconciled", n = 100) => q(
106
+ listBankReconciliationTransactions: (e = "Unreconciled", n = 100) => M(
107
107
  `/bank-reconciliation/transactions${Ee({ status: e, limit: n })}`
108
108
  ),
109
- getBankReconciliationSuggestions: (e, n = 12) => q(
109
+ getBankReconciliationSuggestions: (e, n = 12) => M(
110
110
  `/bank-reconciliation/transactions/${encodeURIComponent(e)}/suggestions${Ee({ limit: n })}`
111
111
  ),
112
- reconcileBankPayment: (e) => q("/bank-reconciliation/payment", { method: "POST", body: JSON.stringify(e) }),
113
- reconcileBankJournal: (e) => q("/bank-reconciliation/journal", { method: "POST", body: JSON.stringify(e) }),
114
- reconcileBankExistingVoucher: (e) => q("/bank-reconciliation/match-existing", { method: "POST", body: JSON.stringify(e) }),
115
- undoBankReconciliation: (e) => q("/bank-reconciliation/undo", {
112
+ reconcileBankPayment: (e) => M("/bank-reconciliation/payment", { method: "POST", body: JSON.stringify(e) }),
113
+ reconcileBankJournal: (e) => M("/bank-reconciliation/journal", { method: "POST", body: JSON.stringify(e) }),
114
+ reconcileBankExistingVoucher: (e) => M("/bank-reconciliation/match-existing", { method: "POST", body: JSON.stringify(e) }),
115
+ undoBankReconciliation: (e) => M("/bank-reconciliation/undo", {
116
116
  method: "POST",
117
117
  body: JSON.stringify({ bank_transaction: e, confirmed: !0 })
118
118
  }),
119
119
  // Chat
120
- createChatSession: () => q(
120
+ createChatSession: () => M(
121
121
  "/chat/sessions",
122
122
  { method: "POST" }
123
123
  ),
124
- getChatSession: (e) => q(
124
+ getChatSession: (e) => M(
125
125
  `/chat/sessions/${encodeURIComponent(e)}`
126
126
  ),
127
127
  // Documents
128
- documentFields: (e) => q(`/documents/${encodeURIComponent(e)}/fields`),
129
- generateDocumentPdf: (e, n) => q(`/documents/${encodeURIComponent(e)}/${encodeURIComponent(n)}/pdf`, { method: "POST" }),
130
- listDocuments: (e, n) => q(
128
+ documentFields: (e) => M(`/documents/${encodeURIComponent(e)}/fields`),
129
+ generateDocumentPdf: (e, n) => M(`/documents/${encodeURIComponent(e)}/${encodeURIComponent(n)}/pdf`, { method: "POST" }),
130
+ listDocuments: (e, n) => M(
131
131
  `/documents/${e}${Ee(n)}`
132
132
  ),
133
- getDocument: (e, n) => q(`/documents/${e}/${encodeURIComponent(n)}`),
133
+ getDocument: (e, n) => M(`/documents/${e}/${encodeURIComponent(n)}`),
134
134
  // Prev/next record around `name` in the same order+filters the list uses.
135
- adjacentDocument: (e, n, a) => q(
135
+ adjacentDocument: (e, n, a) => M(
136
136
  `/documents/${e}/${encodeURIComponent(n)}/adjacent${Ee(a)}`
137
137
  ),
138
138
  // Prev/next master record around `name` in the same order+filters as its list.
139
- adjacentMaster: (e, n, a) => q(
139
+ adjacentMaster: (e, n, a) => M(
140
140
  `/masters/${e}/${encodeURIComponent(n)}/adjacent${Ee(a)}`
141
141
  ),
142
- createDocument: (e, n) => q(`/documents/${e}`, { method: "POST", body: JSON.stringify(n) }),
143
- updateDocument: (e, n, a) => q(`/documents/${e}/${encodeURIComponent(n)}`, {
142
+ createDocument: (e, n) => M(`/documents/${e}`, { method: "POST", body: JSON.stringify(n) }),
143
+ updateDocument: (e, n, a) => M(`/documents/${e}/${encodeURIComponent(n)}`, {
144
144
  method: "PUT",
145
145
  body: JSON.stringify(a)
146
146
  }),
147
- submitDocument: (e, n) => q(`/documents/${e}/${encodeURIComponent(n)}/submit`, { method: "POST" }),
148
- cancelDocument: (e, n) => q(`/documents/${e}/${encodeURIComponent(n)}/cancel`, { method: "POST" }),
149
- discardDocument: (e, n) => q(`/documents/${e}/${encodeURIComponent(n)}/discard`, { method: "POST" }),
150
- convertDocument: (e, n, a) => q(`/documents/${e}/${encodeURIComponent(n)}/convert`, {
147
+ submitDocument: (e, n) => M(`/documents/${e}/${encodeURIComponent(n)}/submit`, { method: "POST" }),
148
+ cancelDocument: (e, n) => M(`/documents/${e}/${encodeURIComponent(n)}/cancel`, { method: "POST" }),
149
+ discardDocument: (e, n) => M(`/documents/${e}/${encodeURIComponent(n)}/discard`, { method: "POST" }),
150
+ convertDocument: (e, n, a) => M(`/documents/${e}/${encodeURIComponent(n)}/convert`, {
151
151
  method: "POST",
152
152
  body: JSON.stringify({ target_doctype: a })
153
153
  }),
154
154
  // Proposal (Sammelofferte) extras — CRUD goes through the document methods
155
155
  // above (doctype "proposal"); these cover the appendix blob + cover default.
156
- getProposalCoverDefault: (e, n) => q(
156
+ getProposalCoverDefault: (e, n) => M(
157
157
  `/proposals/cover-default${Ee({ company: e, customer: n })}`
158
158
  ),
159
159
  uploadProposalAppendix: async (e, n) => {
@@ -172,84 +172,84 @@ const O = {
172
172
  }
173
173
  return i.json();
174
174
  },
175
- deleteProposalAppendix: (e) => q(`/proposals/${encodeURIComponent(e)}/appendix`, { method: "DELETE" }),
175
+ deleteProposalAppendix: (e) => M(`/proposals/${encodeURIComponent(e)}/appendix`, { method: "DELETE" }),
176
176
  proposalPdfUrl: (e) => `${ot}/documents/proposal/${encodeURIComponent(e)}/pdf`,
177
177
  // Masters
178
- listMasters: (e, n) => q(
178
+ listMasters: (e, n) => M(
179
179
  `/masters/${e}${Ee(n)}`
180
180
  ),
181
- searchMasters: (e, n) => q(`/masters/${e}/search?q=${encodeURIComponent(n)}`),
181
+ searchMasters: (e, n) => M(`/masters/${e}/search?q=${encodeURIComponent(n)}`),
182
182
  // Distinct values of one list column — used by dropdown filters and the
183
183
  // shared field-aware search autocomplete.
184
- masterFilterValues: (e, n, a = "", i = 200) => q(
184
+ masterFilterValues: (e, n, a = "", i = 200) => M(
185
185
  `/masters/${e}/filter-values${Ee({ field: n, q: a || void 0, limit: i })}`
186
186
  ),
187
- documentFilterValues: (e, n, a = "", i = 200) => q(
187
+ documentFilterValues: (e, n, a = "", i = 200) => M(
188
188
  `/documents/${e}/filter-values${Ee({ field: n, q: a || void 0, limit: i })}`
189
189
  ),
190
- searchDocuments: (e, n) => q(`/documents/${e}/search?q=${encodeURIComponent(n)}`),
190
+ searchDocuments: (e, n) => M(`/documents/${e}/search?q=${encodeURIComponent(n)}`),
191
191
  searchLink: async (e, n) => {
192
192
  try {
193
- const a = await q(`/masters/${e}/search?q=${encodeURIComponent(n)}`);
193
+ const a = await M(`/masters/${e}/search?q=${encodeURIComponent(n)}`);
194
194
  if (a.length > 0) return a;
195
195
  } catch {
196
196
  }
197
- return q(`/documents/${e}/search?q=${encodeURIComponent(n)}`);
197
+ return M(`/documents/${e}/search?q=${encodeURIComponent(n)}`);
198
198
  },
199
- getMaster: (e, n) => q(`/masters/${e}/${encodeURIComponent(n)}`),
200
- currencies: (e) => q(
199
+ getMaster: (e, n) => M(`/masters/${e}/${encodeURIComponent(n)}`),
200
+ currencies: (e) => M(
201
201
  `/accounting/currencies${Ee({ company: e })}`
202
202
  ),
203
- createMaster: (e, n) => q(`/masters/${e}`, { method: "POST", body: JSON.stringify(n) }),
204
- updateMaster: (e, n, a) => q(`/masters/${e}/${encodeURIComponent(n)}`, {
203
+ createMaster: (e, n) => M(`/masters/${e}`, { method: "POST", body: JSON.stringify(n) }),
204
+ updateMaster: (e, n, a) => M(`/masters/${e}/${encodeURIComponent(n)}`, {
205
205
  method: "PUT",
206
206
  body: JSON.stringify(a)
207
207
  }),
208
- runAction: (e, n) => q(`/actions/${encodeURIComponent(e)}`, {
208
+ runAction: (e, n) => M(`/actions/${encodeURIComponent(e)}`, {
209
209
  method: "POST",
210
210
  body: JSON.stringify(n)
211
211
  }),
212
- deleteMaster: (e, n) => q(`/masters/${e}/${encodeURIComponent(n)}`, { method: "DELETE" }),
212
+ deleteMaster: (e, n) => M(`/masters/${e}/${encodeURIComponent(n)}`, { method: "DELETE" }),
213
213
  // Reports
214
- trialBalance: (e) => q(
214
+ trialBalance: (e) => M(
215
215
  `/reports/trial-balance${Ee(e)}`
216
216
  ),
217
- chartOfAccounts: (e) => q(
217
+ chartOfAccounts: (e) => M(
218
218
  `/reports/chart-of-accounts${Ee(e)}`
219
219
  ),
220
- generalLedger: (e) => q(`/reports/general-ledger${Ee(e)}`),
221
- stockBalance: (e) => q(`/reports/stock-balance${Ee(e)}`),
220
+ generalLedger: (e) => M(`/reports/general-ledger${Ee(e)}`),
221
+ stockBalance: (e) => M(`/reports/stock-balance${Ee(e)}`),
222
222
  // --- Rentals: availability + fleet calendar ---
223
- availability: (e) => q(`/availability${Ee(e)}`),
224
- fleetCalendar: (e) => q(`/availability/calendar${Ee(e)}`),
225
- dashboardSummary: (e) => q(`/reports/dashboard-summary${Ee({ company: e })}`),
226
- profitAndLoss: (e) => q(`/reports/profit-and-loss${Ee(e)}`),
227
- balanceSheet: (e) => q(`/reports/balance-sheet${Ee(e)}`),
228
- arAging: (e) => q(`/reports/ar-aging${Ee(e)}`),
229
- apAging: (e) => q(`/reports/ap-aging${Ee(e)}`),
230
- analyticsMetrics: () => q("/reports/analytics/metrics"),
231
- analytics: (e) => q(`/reports/analytics${Ee(e)}`),
232
- runtimeDatasets: () => q("/reports/runtime/datasets"),
233
- runtimeData: (e) => q("/reports/runtime/data", { method: "POST", body: JSON.stringify(e) }),
234
- createRuntimeDraft: (e) => q("/reports/runtime/drafts", { method: "POST", body: JSON.stringify(e) }),
235
- getRuntimeDraft: (e) => q(`/reports/runtime/drafts/${encodeURIComponent(e)}`),
236
- listRuntimeDrafts: () => q("/reports/runtime/drafts"),
237
- deleteRuntimeDraft: (e) => q(`/reports/runtime/drafts/${encodeURIComponent(e)}`, {
223
+ availability: (e) => M(`/availability${Ee(e)}`),
224
+ fleetCalendar: (e) => M(`/availability/calendar${Ee(e)}`),
225
+ dashboardSummary: (e) => M(`/reports/dashboard-summary${Ee({ company: e })}`),
226
+ profitAndLoss: (e) => M(`/reports/profit-and-loss${Ee(e)}`),
227
+ balanceSheet: (e) => M(`/reports/balance-sheet${Ee(e)}`),
228
+ arAging: (e) => M(`/reports/ar-aging${Ee(e)}`),
229
+ apAging: (e) => M(`/reports/ap-aging${Ee(e)}`),
230
+ analyticsMetrics: () => M("/reports/analytics/metrics"),
231
+ analytics: (e) => M(`/reports/analytics${Ee(e)}`),
232
+ runtimeDatasets: () => M("/reports/runtime/datasets"),
233
+ runtimeData: (e) => M("/reports/runtime/data", { method: "POST", body: JSON.stringify(e) }),
234
+ createRuntimeDraft: (e) => M("/reports/runtime/drafts", { method: "POST", body: JSON.stringify(e) }),
235
+ getRuntimeDraft: (e) => M(`/reports/runtime/drafts/${encodeURIComponent(e)}`),
236
+ listRuntimeDrafts: () => M("/reports/runtime/drafts"),
237
+ deleteRuntimeDraft: (e) => M(`/reports/runtime/drafts/${encodeURIComponent(e)}`, {
238
238
  method: "DELETE"
239
239
  }),
240
240
  // Setup
241
- setupStatus: () => q("/setup/status"),
242
- createCompany: (e) => q("/setup/company", { method: "POST", body: JSON.stringify(e) }),
243
- seedDemo: () => q("/setup/seed-demo", { method: "POST" }),
244
- seedHistory: (e) => q(
241
+ setupStatus: () => M("/setup/status"),
242
+ createCompany: (e) => M("/setup/company", { method: "POST", body: JSON.stringify(e) }),
243
+ seedDemo: () => M("/setup/seed-demo", { method: "POST" }),
244
+ seedHistory: (e) => M(
245
245
  "/setup/seed-history",
246
246
  { method: "POST", body: JSON.stringify(e ?? {}) }
247
247
  ),
248
- importAccountBalances: (e) => q("/setup/opening-balances/accounts", { method: "POST", body: JSON.stringify(e) }),
249
- importStockBalances: (e) => q("/setup/opening-balances/stock", { method: "POST", body: JSON.stringify(e) }),
250
- importOutstandingInvoices: (e) => q("/setup/opening-balances/invoices", { method: "POST", body: JSON.stringify(e) }),
248
+ importAccountBalances: (e) => M("/setup/opening-balances/accounts", { method: "POST", body: JSON.stringify(e) }),
249
+ importStockBalances: (e) => M("/setup/opening-balances/stock", { method: "POST", body: JSON.stringify(e) }),
250
+ importOutstandingInvoices: (e) => M("/setup/opening-balances/invoices", { method: "POST", body: JSON.stringify(e) }),
251
251
  // Auth
252
- authSetupStatus: () => q("/auth/setup-status"),
252
+ authSetupStatus: () => M("/auth/setup-status"),
253
253
  // Social login is a full-page navigation, not a fetch — build the URL the
254
254
  // browser should be sent to. `link` starts a "link to my account" flow.
255
255
  oauthLoginUrl: (e, n) => {
@@ -258,49 +258,49 @@ const O = {
258
258
  const i = a.toString() ? `?${a.toString()}` : "";
259
259
  return Wa(`/auth/${encodeURIComponent(e)}/login${i}`);
260
260
  },
261
- oauthListIdentities: () => q("/auth/oauth/identities"),
262
- authRegister: (e) => q("/auth/register", { method: "POST", body: JSON.stringify(e) }),
263
- authLogin: (e, n) => q("/auth/login", { method: "POST", body: JSON.stringify({ email: e, password: n }) }),
264
- authLogout: () => q("/auth/logout", { method: "POST" }),
265
- authMe: () => q("/auth/me"),
266
- authChangePassword: (e, n) => q("/auth/change-password", {
261
+ oauthListIdentities: () => M("/auth/oauth/identities"),
262
+ authRegister: (e) => M("/auth/register", { method: "POST", body: JSON.stringify(e) }),
263
+ authLogin: (e, n) => M("/auth/login", { method: "POST", body: JSON.stringify({ email: e, password: n }) }),
264
+ authLogout: () => M("/auth/logout", { method: "POST" }),
265
+ authMe: () => M("/auth/me"),
266
+ authChangePassword: (e, n) => M("/auth/change-password", {
267
267
  method: "POST",
268
268
  body: JSON.stringify({ current_password: e, new_password: n })
269
269
  }),
270
- authSetPassword: (e) => q("/auth/set-password", {
270
+ authSetPassword: (e) => M("/auth/set-password", {
271
271
  method: "POST",
272
272
  body: JSON.stringify({ new_password: e })
273
273
  }),
274
- authInvite: (e, n) => q("/auth/invite", { method: "POST", body: JSON.stringify({ email: e, role: n }) }),
275
- authListUsers: () => q("/auth/users"),
276
- authChangeRole: (e, n) => q(`/auth/users/${encodeURIComponent(e)}/role`, { method: "PUT", body: JSON.stringify({ role: n }) }),
277
- authDisableUser: (e) => q(`/auth/users/${encodeURIComponent(e)}`, { method: "DELETE" }),
278
- authListInvites: () => q("/auth/invites"),
279
- authRevokeInvite: (e) => q(`/auth/invites/${encodeURIComponent(e)}`, { method: "DELETE" }),
280
- getPublicManagerStatus: () => q("/auth/public-manager"),
281
- createPublicManager: () => q("/auth/public-manager", { method: "POST" }),
282
- removePublicManager: () => q("/auth/public-manager", { method: "DELETE" }),
283
- getHealth: () => q("/health"),
284
- getSettings: () => q("/auth/settings"),
285
- updateSettings: (e) => q("/auth/settings", { method: "PUT", body: JSON.stringify(e) }),
274
+ authInvite: (e, n) => M("/auth/invite", { method: "POST", body: JSON.stringify({ email: e, role: n }) }),
275
+ authListUsers: () => M("/auth/users"),
276
+ authChangeRole: (e, n) => M(`/auth/users/${encodeURIComponent(e)}/role`, { method: "PUT", body: JSON.stringify({ role: n }) }),
277
+ authDisableUser: (e) => M(`/auth/users/${encodeURIComponent(e)}`, { method: "DELETE" }),
278
+ authListInvites: () => M("/auth/invites"),
279
+ authRevokeInvite: (e) => M(`/auth/invites/${encodeURIComponent(e)}`, { method: "DELETE" }),
280
+ getPublicManagerStatus: () => M("/auth/public-manager"),
281
+ createPublicManager: () => M("/auth/public-manager", { method: "POST" }),
282
+ removePublicManager: () => M("/auth/public-manager", { method: "DELETE" }),
283
+ getHealth: () => M("/health"),
284
+ getSettings: () => M("/auth/settings"),
285
+ updateSettings: (e) => M("/auth/settings", { method: "PUT", body: JSON.stringify(e) }),
286
286
  // Per-USER preferences (columns.<doctype>, language, …) — scoped to the caller,
287
287
  // not admin-gated, unlike the global getSettings/updateSettings above.
288
- getMySettings: () => q("/auth/my-settings"),
289
- updateMySettings: (e) => q("/auth/my-settings", { method: "PUT", body: JSON.stringify(e) }),
290
- getConnectionStatus: () => q("/mcp-oauth/status"),
291
- getMcpConsent: (e) => q(`/mcp-oauth/consent?request_id=${encodeURIComponent(e)}`),
292
- approveMcp: (e, n, a, i) => q("/mcp-oauth/consent", { method: "POST", body: JSON.stringify({ request_id: e, csrf_token: n, allow: a, role: i }) }),
293
- getApiKeys: () => q("/auth/api-keys"),
294
- createApiKey: (e, n, a = "other") => q("/auth/api-keys", { method: "POST", body: JSON.stringify({ name: e, role: n, app_type: a }) }),
295
- revokeApiKey: (e) => q(
288
+ getMySettings: () => M("/auth/my-settings"),
289
+ updateMySettings: (e) => M("/auth/my-settings", { method: "PUT", body: JSON.stringify(e) }),
290
+ getConnectionStatus: () => M("/mcp-oauth/status"),
291
+ getMcpConsent: (e) => M(`/mcp-oauth/consent?request_id=${encodeURIComponent(e)}`),
292
+ approveMcp: (e, n, a, i) => M("/mcp-oauth/consent", { method: "POST", body: JSON.stringify({ request_id: e, csrf_token: n, allow: a, role: i }) }),
293
+ getApiKeys: () => M("/auth/api-keys"),
294
+ createApiKey: (e, n, a = "other") => M("/auth/api-keys", { method: "POST", body: JSON.stringify({ name: e, role: n, app_type: a }) }),
295
+ revokeApiKey: (e) => M(
296
296
  `/auth/api-keys/${encodeURIComponent(e)}/revoke`,
297
297
  { method: "POST" }
298
298
  ),
299
- deleteApiKey: (e) => q(
299
+ deleteApiKey: (e) => M(
300
300
  `/auth/api-keys/${encodeURIComponent(e)}`,
301
301
  { method: "DELETE" }
302
302
  ),
303
- getDemoSpend: () => q("/admin/demo-spend")
303
+ getDemoSpend: () => M("/admin/demo-spend")
304
304
  }, Ha = An(null);
305
305
  function ki({ children: e }) {
306
306
  const [n, a] = w(null), [i, o] = w(!0);
@@ -761,7 +761,7 @@ function Ci({ children: e }) {
761
761
  } catch {
762
762
  }
763
763
  }
764
- async function qe(N) {
764
+ async function Me(N) {
765
765
  await re("delete_session", { session_id: N }, "session_deleted");
766
766
  }
767
767
  async function Te(N) {
@@ -826,7 +826,7 @@ function Ci({ children: e }) {
826
826
  function A(N) {
827
827
  return N ? b[N] ?? "idle" : "idle";
828
828
  }
829
- function M(N) {
829
+ function q(N) {
830
830
  return N ? S[N] ?? null : null;
831
831
  }
832
832
  return /* @__PURE__ */ t(
@@ -840,7 +840,7 @@ function Ci({ children: e }) {
840
840
  createSession: be,
841
841
  startDemo: z,
842
842
  completeDemoTyping: ne,
843
- deleteSession: qe,
843
+ deleteSession: Me,
844
844
  loadHistory: Te,
845
845
  loadMoreHistory: je,
846
846
  clearHistory: We,
@@ -851,7 +851,7 @@ function Ci({ children: e }) {
851
851
  isLoadingOlder: De,
852
852
  isThinking: U,
853
853
  getDemoStatus: A,
854
- getDemoTypingCue: M
854
+ getDemoTypingCue: q
855
855
  },
856
856
  children: e
857
857
  }
@@ -1642,15 +1642,15 @@ function zi() {
1642
1642
  )
1643
1643
  ] })
1644
1644
  ] }),
1645
- /* @__PURE__ */ t("main", { className: "flex-1 overflow-auto p-4 md:p-6", children: /* @__PURE__ */ t(qr, {}) })
1645
+ /* @__PURE__ */ t("main", { className: "flex-1 overflow-auto p-4 md:p-6", children: /* @__PURE__ */ t(Mr, {}) })
1646
1646
  ] })
1647
1647
  ] });
1648
1648
  }
1649
- function qi({ children: e }) {
1649
+ function Mi({ children: e }) {
1650
1650
  const { user: n, loading: a } = He();
1651
1651
  return a ? /* @__PURE__ */ t("div", { className: "flex h-dvh items-center justify-center", children: /* @__PURE__ */ t("div", { className: "text-gray-400", children: "Loading..." }) }) : n ? /* @__PURE__ */ t(ke, { children: e }) : /* @__PURE__ */ t(It, { to: "/login", replace: !0 });
1652
1652
  }
1653
- function Mi({ children: e }) {
1653
+ function qi({ children: e }) {
1654
1654
  const { user: n, loading: a } = He();
1655
1655
  return a ? /* @__PURE__ */ t("div", { className: "flex h-dvh items-center justify-center", children: /* @__PURE__ */ t("div", { className: "text-gray-400", children: "Loading..." }) }) : n ? n.role !== "admin" ? /* @__PURE__ */ t(It, { to: "/", replace: !0 }) : /* @__PURE__ */ t(ke, { children: e }) : /* @__PURE__ */ t(It, { to: "/login", replace: !0 });
1656
1656
  }
@@ -1753,7 +1753,7 @@ function zt({ title: e, value: n, icon: a, tone: i }) {
1753
1753
  /* @__PURE__ */ t("div", { className: `shrink-0 rounded-lg p-2 ${o}`, children: /* @__PURE__ */ t(a, { className: "h-5 w-5", strokeWidth: 2 }) })
1754
1754
  ] }) });
1755
1755
  }
1756
- function qt() {
1756
+ function Mt() {
1757
1757
  return /* @__PURE__ */ t(G, { children: /* @__PURE__ */ r("div", { className: "flex items-start justify-between gap-4", children: [
1758
1758
  /* @__PURE__ */ r("div", { className: "min-w-0 flex-1", children: [
1759
1759
  /* @__PURE__ */ t("div", { className: "h-4 w-24 animate-pulse rounded bg-surface-subtle" }),
@@ -1808,10 +1808,10 @@ function ji() {
1808
1808
  });
1809
1809
  return /* @__PURE__ */ r("div", { className: "space-y-6", children: [
1810
1810
  /* @__PURE__ */ t("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: a ? /* @__PURE__ */ r(ke, { children: [
1811
- /* @__PURE__ */ t(qt, {}),
1812
- /* @__PURE__ */ t(qt, {}),
1813
- /* @__PURE__ */ t(qt, {}),
1814
- /* @__PURE__ */ t(qt, {})
1811
+ /* @__PURE__ */ t(Mt, {}),
1812
+ /* @__PURE__ */ t(Mt, {}),
1813
+ /* @__PURE__ */ t(Mt, {}),
1814
+ /* @__PURE__ */ t(Mt, {})
1815
1815
  ] }) : /* @__PURE__ */ r(ke, { children: [
1816
1816
  /* @__PURE__ */ t(
1817
1817
  zt,
@@ -3399,21 +3399,21 @@ function aa() {
3399
3399
  }), ee = xe({
3400
3400
  mutationFn: (z) => O.convertDocument(e, n, z),
3401
3401
  onSuccess: (z, ne) => {
3402
- const qe = ne.toLowerCase().replace(/\s+/g, "-");
3403
- a(`/app/${qe}/${z.name}`);
3402
+ const Me = ne.toLowerCase().replace(/\s+/g, "-");
3403
+ a(`/app/${Me}/${z.name}`);
3404
3404
  }
3405
3405
  }), $ = Ve(
3406
3406
  (z, ne) => {
3407
- z === "currency" && f(!0), p((qe) => {
3408
- const Te = { ...qe, [z]: ne };
3407
+ z === "currency" && f(!0), p((Me) => {
3408
+ const Te = { ...Me, [z]: ne };
3409
3409
  return z === "currency" && (s == null ? void 0 : s.usesExchangeRate) !== !1 && (Te.conversion_rate = 0), e === "reservation" && z === "asset" && ne && (Te.allocation_mode = "Unit"), na(Te, s);
3410
3410
  });
3411
3411
  },
3412
3412
  [s, e]
3413
3413
  ), H = Ve(
3414
3414
  (z, ne) => {
3415
- p((qe) => {
3416
- const Te = { ...qe, [z]: ne };
3415
+ p((Me) => {
3416
+ const Te = { ...Me, [z]: ne };
3417
3417
  return na(Te, s);
3418
3418
  });
3419
3419
  },
@@ -3664,7 +3664,7 @@ function is(e, n) {
3664
3664
  function ra(e) {
3665
3665
  return typeof e == "object" ? e.label : String(e);
3666
3666
  }
3667
- function Mt(e) {
3667
+ function qt(e) {
3668
3668
  return typeof e == "object" ? e.value : String(e);
3669
3669
  }
3670
3670
  function ur({
@@ -3829,13 +3829,13 @@ function ur({
3829
3829
  type: "button",
3830
3830
  role: "option",
3831
3831
  className: "flex w-full items-center justify-between rounded-md px-3 py-2 text-left text-sm hover:bg-surface-subtle",
3832
- onClick: () => V(Mt(P)),
3832
+ onClick: () => V(qt(P)),
3833
3833
  children: [
3834
3834
  /* @__PURE__ */ t("span", { className: "text-fg", children: ra(P) }),
3835
- ra(P) !== Mt(P) && /* @__PURE__ */ t("code", { className: "ml-4 text-xs text-fg-muted", children: Mt(P) })
3835
+ ra(P) !== qt(P) && /* @__PURE__ */ t("code", { className: "ml-4 text-xs text-fg-muted", children: qt(P) })
3836
3836
  ]
3837
3837
  },
3838
- Mt(P)
3838
+ qt(P)
3839
3839
  )),
3840
3840
  !J && H && _.length === 0 && /* @__PURE__ */ t("div", { className: "px-3 py-4 text-sm text-fg-muted", children: f("smartSearch.noValues") }),
3841
3841
  !!h.value && /* @__PURE__ */ t(
@@ -4035,37 +4035,37 @@ function us() {
4035
4035
  const { t: e } = ie(), n = Ye(), { doctype: a } = bt(), i = sr(a ?? ""), o = Ct();
4036
4036
  ze((i == null ? void 0 : i.label) ?? a ?? null);
4037
4037
  const s = (i == null ? void 0 : i.listFilters) ?? [], l = fe(() => {
4038
- const A = new URLSearchParams(o.search), M = {}, N = /* @__PURE__ */ new Set(["q", "page", "per_page", "from", "to", "discarded", "order_by", "order"]);
4038
+ const A = new URLSearchParams(o.search), q = {}, N = /* @__PURE__ */ new Set(["q", "page", "per_page", "from", "to", "discarded", "order_by", "order"]);
4039
4039
  for (const [T, K] of A.entries())
4040
- !N.has(T) && K && K !== "All" && (M[T] = K);
4041
- return M;
4040
+ !N.has(T) && K && K !== "All" && (q[T] = K);
4041
+ return q;
4042
4042
  }, [o.search]), c = fe(() => {
4043
4043
  const A = {};
4044
- for (const M of s) A[M] = l[M] ?? "";
4044
+ for (const q of s) A[q] = l[q] ?? "";
4045
4045
  return A;
4046
4046
  }, [s, l]), [p] = he("status", "All"), [u] = he("from", ""), [f] = he("to", ""), [m] = he("discarded", ""), [v] = he("per_page", 50), x = (i == null ? void 0 : i.searchFields) ?? [], [b] = he("q", ""), [g, S] = w(b), [C] = he("page", 1), k = C - 1, h = lt(), { settings: y, setSetting: R } = Xt(), [E] = he("order_by", ""), [ee] = he("order", ""), [$, H] = (y[`sort.${a}`] || "").split(":"), Q = E || $ || "", J = (E ? ee : H) || "desc", _ = (A) => {
4047
- const M = Q === A ? J === "desc" ? "asc" : "desc" : la(A) ? "desc" : "asc";
4048
- h({ order_by: A, order: M, page: null }), R(`sort.${a}`, `${A}:${M}`);
4047
+ const q = Q === A ? J === "desc" ? "asc" : "desc" : la(A) ? "desc" : "asc";
4048
+ h({ order_by: A, order: q, page: null }), R(`sort.${a}`, `${A}:${q}`);
4049
4049
  }, [D, V] = w(!1), d = pe(null), L = fe(
4050
4050
  () => (y[`columns.${a}`] || "").split(",").map((A) => A.trim()).filter(Boolean),
4051
4051
  [y, a]
4052
4052
  ), F = L.length ? L : (i == null ? void 0 : i.listColumns) ?? [], P = F.length ? [...F, "currency", "party_type"].join(",") : void 0, j = fe(() => {
4053
4053
  if (!i) return [];
4054
- const A = /* @__PURE__ */ new Set(), M = [], N = (T, K) => {
4055
- A.has(T) || (A.add(T), M.push({ name: T, label: K }));
4054
+ const A = /* @__PURE__ */ new Set(), q = [], N = (T, K) => {
4055
+ A.has(T) || (A.add(T), q.push({ name: T, label: K }));
4056
4056
  };
4057
4057
  N("name", e("fields.Name", { defaultValue: "Name" }));
4058
4058
  for (const T of i.fields) N(T.name, e(`fields.${T.label}`, { defaultValue: T.label }));
4059
4059
  for (const T of Gt) N(T.name, e(`fields.${T.label}`, { defaultValue: T.label }));
4060
- return M;
4060
+ return q;
4061
4061
  }, [i, e]), se = (A) => {
4062
- const M = F.includes(A) ? F.filter((N) => N !== A) : [...F, A];
4063
- M.length !== 0 && R(`columns.${a}`, M.join(","));
4062
+ const q = F.includes(A) ? F.filter((N) => N !== A) : [...F, A];
4063
+ q.length !== 0 && R(`columns.${a}`, q.join(","));
4064
4064
  };
4065
4065
  ce(() => {
4066
4066
  if (!D) return;
4067
- const A = (M) => {
4068
- d.current && !d.current.contains(M.target) && V(!1);
4067
+ const A = (q) => {
4068
+ d.current && !d.current.contains(q.target) && V(!1);
4069
4069
  };
4070
4070
  return document.addEventListener("mousedown", A), () => document.removeEventListener("mousedown", A);
4071
4071
  }, [D]);
@@ -4076,12 +4076,12 @@ function us() {
4076
4076
  const be = fe(() => {
4077
4077
  const A = {};
4078
4078
  s.length === 0 && p !== "All" && (A.status = p), u && (A.from_date = u), f && (A.to_date = f), i != null && i.dateField && (A.date_field = i.dateField);
4079
- for (const M of s)
4080
- c[M] && (A[M] = c[M]);
4081
- for (const [M, N] of Object.entries(l))
4082
- A[M] = N;
4079
+ for (const q of s)
4080
+ c[q] && (A[q] = c[q]);
4081
+ for (const [q, N] of Object.entries(l))
4082
+ A[q] = N;
4083
4083
  return x.length > 0 && b && (A.search = b, A.search_fields = x.join(",")), m && (A.include_discarded = "true"), Q && (A.order_by = Q, A.order = J), P && (A.fields = P), A.limit = v, A.offset = k * v, A;
4084
- }, [p, u, f, m, v, k, i == null ? void 0 : i.dateField, s, c, l, x, b, Q, J, P]), { data: z, isLoading: ne, refetch: qe } = Wi(a ?? "", be), Te = (z == null ? void 0 : z.rows) ?? [], je = (z == null ? void 0 : z.total) ?? 0, We = fe(() => new Set((z == null ? void 0 : z.text_fields) ?? []), [z == null ? void 0 : z.text_fields]), Je = fe(() => {
4084
+ }, [p, u, f, m, v, k, i == null ? void 0 : i.dateField, s, c, l, x, b, Q, J, P]), { data: z, isLoading: ne, refetch: Me } = Wi(a ?? "", be), Te = (z == null ? void 0 : z.rows) ?? [], je = (z == null ? void 0 : z.total) ?? 0, We = fe(() => new Set((z == null ? void 0 : z.text_fields) ?? []), [z == null ? void 0 : z.text_fields]), Je = fe(() => {
4085
4085
  if (!i) return [];
4086
4086
  const A = new Map(i.fields.map((N) => [N.name, N]));
4087
4087
  return [.../* @__PURE__ */ new Set([
@@ -4106,12 +4106,12 @@ function us() {
4106
4106
  });
4107
4107
  }, [j, i, s, x, e, We]);
4108
4108
  ce(() => {
4109
- const { limit: A, offset: M, ...N } = be;
4109
+ const { limit: A, offset: q, ...N } = be;
4110
4110
  ir(a ?? "", { filters: N, search: o.search });
4111
4111
  }, [a, be, o.search]);
4112
4112
  const it = Math.max(1, Math.ceil(je / v)), Ie = je === 0 ? 0 : k * v + 1, U = Math.min(je, (k + 1) * v), ae = fe(() => {
4113
4113
  if (!i) return [];
4114
- const A = $a(), M = (N) => {
4114
+ const A = $a(), q = (N) => {
4115
4115
  const T = Gt.find((oe) => oe.name === N);
4116
4116
  if (T) return e(`fields.${T.label}`, { defaultValue: T.label });
4117
4117
  const K = N.split("_").map((oe) => oe.charAt(0).toUpperCase() + oe.slice(1)).join(" ");
@@ -4137,7 +4137,7 @@ function us() {
4137
4137
  });
4138
4138
  if (ls.has(N))
4139
4139
  return A.accessor(N, {
4140
- header: M(N),
4140
+ header: q(N),
4141
4141
  cell: (T) => {
4142
4142
  var K;
4143
4143
  return Qe(T.getValue(), ((K = T.row.original) == null ? void 0 : K.currency) || n);
@@ -4164,7 +4164,7 @@ function us() {
4164
4164
  if (ca[N]) {
4165
4165
  const T = ca[N];
4166
4166
  return A.accessor(N, {
4167
- header: M(N),
4167
+ header: q(N),
4168
4168
  cell: (K) => {
4169
4169
  const oe = K.getValue();
4170
4170
  if (!oe) return "-";
@@ -4182,13 +4182,13 @@ function us() {
4182
4182
  });
4183
4183
  }
4184
4184
  return N.endsWith("_datetime") ? A.accessor(N, {
4185
- header: M(N),
4185
+ header: q(N),
4186
4186
  cell: (T) => Tn(T.getValue())
4187
4187
  }) : pr.has(N) || cs.has(N) ? A.accessor(N, {
4188
- header: M(N),
4188
+ header: q(N),
4189
4189
  cell: (T) => rt(T.getValue())
4190
4190
  }) : A.accessor(N, {
4191
- header: M(N)
4191
+ header: q(N)
4192
4192
  });
4193
4193
  });
4194
4194
  }, [i, e, n, F]), De = Va({
@@ -4206,7 +4206,7 @@ function us() {
4206
4206
  D && /* @__PURE__ */ r("div", { className: "absolute right-0 z-20 mt-2 max-h-80 w-60 overflow-y-auto rounded-lg border border-line bg-surface p-2 shadow-card", children: [
4207
4207
  /* @__PURE__ */ t("div", { className: "px-2 py-1 text-xs font-medium uppercase tracking-wide text-fg-muted", children: e("common.columns", { defaultValue: "Columns" }) }),
4208
4208
  j.map((A) => {
4209
- const M = F.includes(A.name), N = M && F.length === 1;
4209
+ const q = F.includes(A.name), N = q && F.length === 1;
4210
4210
  return /* @__PURE__ */ r(
4211
4211
  "label",
4212
4212
  {
@@ -4217,7 +4217,7 @@ function us() {
4217
4217
  {
4218
4218
  type: "checkbox",
4219
4219
  className: "h-4 w-4 rounded border-line text-brand focus:ring-brand/30 disabled:opacity-50",
4220
- checked: M,
4220
+ checked: q,
4221
4221
  disabled: N,
4222
4222
  onChange: () => se(A.name)
4223
4223
  }
@@ -4242,11 +4242,11 @@ function us() {
4242
4242
  fields: Je,
4243
4243
  filters: l,
4244
4244
  onFiltersChange: (A) => h({ ...A, page: null }),
4245
- onSubmit: (A, M) => {
4246
- const N = k === 0 && A === b && Object.entries(M).every(([T, K]) => (l[T] ?? "") === (K === "All" ? "" : K || ""));
4247
- h({ q: A || null, ...M, page: null }), N && qe();
4245
+ onSubmit: (A, q) => {
4246
+ const N = k === 0 && A === b && Object.entries(q).every(([T, K]) => (l[T] ?? "") === (K === "All" ? "" : K || ""));
4247
+ h({ q: A || null, ...q, page: null }), N && Me();
4248
4248
  },
4249
- loadValues: (A, M) => O.documentFilterValues(a, A, M, 12).then((N) => N.values),
4249
+ loadValues: (A, q) => O.documentFilterValues(a, A, q, 12).then((N) => N.values),
4250
4250
  hiddenChipFields: [
4251
4251
  ...s,
4252
4252
  ...s.length === 0 ? ["status"] : []
@@ -4265,13 +4265,13 @@ function us() {
4265
4265
  onChange: (A) => we(A.target.value)
4266
4266
  }
4267
4267
  ) : s.map((A) => {
4268
- const M = i.fields.find((T) => T.name === A), N = ((M == null ? void 0 : M.options) ?? []).map(
4268
+ const q = i.fields.find((T) => T.name === A), N = ((q == null ? void 0 : q.options) ?? []).map(
4269
4269
  (T) => typeof T == "string" ? { value: T, label: T } : T
4270
4270
  );
4271
4271
  return /* @__PURE__ */ t(
4272
4272
  Ke,
4273
4273
  {
4274
- label: e(`fields.${(M == null ? void 0 : M.label) ?? A}`, { defaultValue: (M == null ? void 0 : M.label) ?? A }),
4274
+ label: e(`fields.${(q == null ? void 0 : q.label) ?? A}`, { defaultValue: (q == null ? void 0 : q.label) ?? A }),
4275
4275
  options: [{ value: "", label: e("common.all") }, ...N],
4276
4276
  value: c[A],
4277
4277
  onChange: (T) => h({ [A]: T.target.value || null, page: null })
@@ -4310,7 +4310,7 @@ function us() {
4310
4310
  e("common.showDiscarded")
4311
4311
  ] })
4312
4312
  ] }),
4313
- /* @__PURE__ */ t(en, { onSelect: (A, M) => h({ from: A, to: M, page: null }) }),
4313
+ /* @__PURE__ */ t(en, { onSelect: (A, q) => h({ from: A, to: q, page: null }) }),
4314
4314
  ne ? /* @__PURE__ */ t("p", { className: "text-fg-muted", children: e("common.loading") }) : Te.length === 0 ? /* @__PURE__ */ t("p", { className: "py-8 text-center text-fg-muted", children: e("reports.noDocuments") }) : /* @__PURE__ */ r(ke, { children: [
4315
4315
  /* @__PURE__ */ t(
4316
4316
  Qt,
@@ -4326,30 +4326,30 @@ function us() {
4326
4326
  }
4327
4327
  ),
4328
4328
  /* @__PURE__ */ t("div", { className: "overflow-x-auto rounded-xl bg-surface ring-1 ring-line shadow-card", children: /* @__PURE__ */ r("table", { className: "min-w-full divide-y divide-line text-sm", children: [
4329
- /* @__PURE__ */ t("thead", { className: "bg-surface-subtle", children: De.getHeaderGroups().map((A) => /* @__PURE__ */ t("tr", { children: A.headers.map((M) => /* @__PURE__ */ t(
4329
+ /* @__PURE__ */ t("thead", { className: "bg-surface-subtle", children: De.getHeaderGroups().map((A) => /* @__PURE__ */ t("tr", { children: A.headers.map((q) => /* @__PURE__ */ t(
4330
4330
  "th",
4331
4331
  {
4332
4332
  className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-fg-muted",
4333
- children: M.isPlaceholder ? null : /* @__PURE__ */ r(
4333
+ children: q.isPlaceholder ? null : /* @__PURE__ */ r(
4334
4334
  "button",
4335
4335
  {
4336
4336
  type: "button",
4337
- onClick: () => _(M.column.id),
4337
+ onClick: () => _(q.column.id),
4338
4338
  title: e("common.sortByColumn", { defaultValue: "Sort by this column" }),
4339
4339
  className: "group inline-flex items-center gap-1 uppercase tracking-wide transition-colors hover:text-fg",
4340
4340
  children: [
4341
- Ht(M.column.columnDef.header, M.getContext()),
4342
- Q === M.column.id ? J === "asc" ? /* @__PURE__ */ t(Oa, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ t(St, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ t(Fa, { className: "h-3.5 w-3.5 opacity-0 transition-opacity group-hover:opacity-40" })
4341
+ Ht(q.column.columnDef.header, q.getContext()),
4342
+ Q === q.column.id ? J === "asc" ? /* @__PURE__ */ t(Oa, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ t(St, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ t(Fa, { className: "h-3.5 w-3.5 opacity-0 transition-opacity group-hover:opacity-40" })
4343
4343
  ]
4344
4344
  }
4345
4345
  )
4346
4346
  },
4347
- M.id
4347
+ q.id
4348
4348
  )) }, A.id)) }),
4349
- /* @__PURE__ */ t("tbody", { className: "divide-y divide-line", children: De.getRowModel().rows.map((A) => /* @__PURE__ */ t("tr", { className: "transition-colors hover:bg-surface-subtle", children: A.getVisibleCells().map((M) => /* @__PURE__ */ t("td", { className: "px-4 py-2.5 text-fg", children: Ht(
4350
- M.column.columnDef.cell,
4351
- M.getContext()
4352
- ) }, M.id)) }, A.id)) })
4349
+ /* @__PURE__ */ t("tbody", { className: "divide-y divide-line", children: De.getRowModel().rows.map((A) => /* @__PURE__ */ t("tr", { className: "transition-colors hover:bg-surface-subtle", children: A.getVisibleCells().map((q) => /* @__PURE__ */ t("td", { className: "px-4 py-2.5 text-fg", children: Ht(
4350
+ q.column.columnDef.cell,
4351
+ q.getContext()
4352
+ ) }, q.id)) }, A.id)) })
4353
4353
  ] }) }),
4354
4354
  /* @__PURE__ */ t(
4355
4355
  Qt,
@@ -4370,7 +4370,7 @@ function us() {
4370
4370
  a
4371
4371
  ] });
4372
4372
  }
4373
- function Me({
4373
+ function qe({
4374
4374
  label: e,
4375
4375
  value: n,
4376
4376
  onChange: a,
@@ -4598,8 +4598,8 @@ function da() {
4598
4598
  /* @__PURE__ */ r("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
4599
4599
  /* @__PURE__ */ t(me, { label: s("fields.Title", "Title"), value: c.title, onChange: (d) => k("title", d.target.value) }),
4600
4600
  /* @__PURE__ */ t(me, { label: s("fields.Date", "Date"), type: "date", value: c.proposal_date, onChange: (d) => k("proposal_date", d.target.value) }),
4601
- /* @__PURE__ */ t(Me, { label: s("fields.Customer", "Customer"), value: c.customer, onChange: (d) => k("customer", d), linkDoctype: "customer", readOnly: !1 }),
4602
- /* @__PURE__ */ t(Me, { label: s("fields.Company", "Company"), value: c.company, onChange: (d) => k("company", d), linkDoctype: "company", readOnly: !1 }),
4601
+ /* @__PURE__ */ t(qe, { label: s("fields.Customer", "Customer"), value: c.customer, onChange: (d) => k("customer", d), linkDoctype: "customer", readOnly: !1 }),
4602
+ /* @__PURE__ */ t(qe, { label: s("fields.Company", "Company"), value: c.company, onChange: (d) => k("company", d), linkDoctype: "company", readOnly: !1 }),
4603
4603
  /* @__PURE__ */ t(me, { label: s("fields.Partner", "Account Manager"), value: c.partner_name, onChange: (d) => k("partner_name", d.target.value) }),
4604
4604
  /* @__PURE__ */ t(me, { label: s("fields.Partner Email", "Account Manager Email"), value: c.partner_email, onChange: (d) => k("partner_email", d.target.value) })
4605
4605
  ] }),
@@ -4627,7 +4627,7 @@ function da() {
4627
4627
  u.map((d, L) => /* @__PURE__ */ r("div", { className: "rounded-lg p-3 ring-1 ring-line", children: [
4628
4628
  /* @__PURE__ */ r("div", { className: "mb-2 flex items-center gap-2", children: [
4629
4629
  /* @__PURE__ */ t("span", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-brand/10 text-sm font-bold text-brand", children: ms[L] ?? L + 1 }),
4630
- /* @__PURE__ */ t("div", { className: "flex-1", children: /* @__PURE__ */ t(Me, { label: "", value: d.quotation, onChange: (F) => $(L, { quotation: F }), linkDoctype: "quotation", readOnly: !1 }) }),
4630
+ /* @__PURE__ */ t("div", { className: "flex-1", children: /* @__PURE__ */ t(qe, { label: "", value: d.quotation, onChange: (F) => $(L, { quotation: F }), linkDoctype: "quotation", readOnly: !1 }) }),
4631
4631
  /* @__PURE__ */ t("button", { type: "button", onClick: () => J(L, -1), disabled: L === 0, className: "px-1 text-fg-muted disabled:opacity-30", children: "↑" }),
4632
4632
  /* @__PURE__ */ t("button", { type: "button", onClick: () => J(L, 1), disabled: L === u.length - 1, className: "px-1 text-fg-muted disabled:opacity-30", children: "↓" }),
4633
4633
  /* @__PURE__ */ t("button", { type: "button", onClick: () => Q(L), className: "px-1 text-red-500 hover:text-red-700", children: "✕" })
@@ -4763,8 +4763,8 @@ function gs() {
4763
4763
  }, [$]);
4764
4764
  const J = (i == null ? void 0 : i.listFilters) ?? hs[e ?? ""] ?? [], _ = fe(() => {
4765
4765
  const U = new URLSearchParams(a.search), ae = {}, De = /* @__PURE__ */ new Set(["q", "page", "per_page", "include_disabled", "order_by", "order"]);
4766
- for (const [A, M] of U.entries())
4767
- !De.has(A) && M && (ae[A] = M);
4766
+ for (const [A, q] of U.entries())
4767
+ !De.has(A) && q && (ae[A] = q);
4768
4768
  return ae;
4769
4769
  }, [a.search, e]), D = fe(() => {
4770
4770
  var U, ae;
@@ -4796,13 +4796,13 @@ function gs() {
4796
4796
  ...J.map((A) => A.field)
4797
4797
  ])].map((A) => {
4798
4798
  var K;
4799
- const M = ae.get(A), N = (M == null ? void 0 : M.label) ?? Pt(A), T = (K = M == null ? void 0 : M.options) == null ? void 0 : K.map(
4799
+ const q = ae.get(A), N = (q == null ? void 0 : q.label) ?? Pt(A), T = (K = q == null ? void 0 : q.options) == null ? void 0 : K.map(
4800
4800
  (oe) => typeof oe == "string" ? { value: oe, label: oe } : { value: oe.value, label: oe.label }
4801
4801
  );
4802
4802
  return {
4803
4803
  name: A,
4804
4804
  label: s(`fields.${N}`, { defaultValue: N }),
4805
- type: (M == null ? void 0 : M.type) ?? (ua(A) ? "date" : "text"),
4805
+ type: (q == null ? void 0 : q.type) ?? (ua(A) ? "date" : "text"),
4806
4806
  options: T,
4807
4807
  contains: j.has(A),
4808
4808
  suggestOnEmpty: !!(T != null && T.length) || J.some((oe) => oe.field === A)
@@ -4811,7 +4811,7 @@ function gs() {
4811
4811
  }, [_e, i, J, s, j, e]), be = fe(
4812
4812
  () => (g[`columns.master:${e}`] || "").split(",").map((U) => U.trim()).filter(Boolean),
4813
4813
  [g, e]
4814
- ).filter((U) => _e.includes(U)), z = ((i == null ? void 0 : i.listColumns) ?? []).filter((U) => _e.includes(U)), ne = be.length ? be : z.length ? z : _e, qe = (U) => {
4814
+ ).filter((U) => _e.includes(U)), z = ((i == null ? void 0 : i.listColumns) ?? []).filter((U) => _e.includes(U)), ne = be.length ? be : z.length ? z : _e, Me = (U) => {
4815
4815
  const ae = ne.includes(U) ? ne.filter((De) => De !== U) : [...ne, U];
4816
4816
  ae.length !== 0 && S(`columns.master:${e}`, ae.join(","));
4817
4817
  }, [Te, je] = w(!1), We = pe(null);
@@ -4829,14 +4829,14 @@ function gs() {
4829
4829
  (ae) => {
4830
4830
  var De, A;
4831
4831
  return U.accessor(ae, {
4832
- header: s(`fields.${((De = i == null ? void 0 : i.fields.find((M) => M.name === ae)) == null ? void 0 : De.label) ?? Pt(ae)}`, {
4833
- defaultValue: ((A = i == null ? void 0 : i.fields.find((M) => M.name === ae)) == null ? void 0 : A.label) ?? Pt(ae)
4832
+ header: s(`fields.${((De = i == null ? void 0 : i.fields.find((q) => q.name === ae)) == null ? void 0 : De.label) ?? Pt(ae)}`, {
4833
+ defaultValue: ((A = i == null ? void 0 : i.fields.find((q) => q.name === ae)) == null ? void 0 : A.label) ?? Pt(ae)
4834
4834
  }),
4835
- cell: (M) => {
4835
+ cell: (q) => {
4836
4836
  var oe, Ne;
4837
- const N = M.getValue(), T = i == null ? void 0 : i.fields.find((Se) => Se.name === ae);
4837
+ const N = q.getValue(), T = i == null ? void 0 : i.fields.find((Se) => Se.name === ae);
4838
4838
  if (ae === "name") {
4839
- const Se = M.row.original.disabled === 1;
4839
+ const Se = q.row.original.disabled === 1;
4840
4840
  return /* @__PURE__ */ t(
4841
4841
  Ce,
4842
4842
  {
@@ -4850,7 +4850,7 @@ function gs() {
4850
4850
  return s(N === 1 ? "common.disabled" : "common.active");
4851
4851
  if ((T == null ? void 0 : T.type) === "date") return rt(N);
4852
4852
  if ((T == null ? void 0 : T.type) === "datetime") return Tn(N);
4853
- const K = (Ne = (oe = i == null ? void 0 : i.columnLinks) == null ? void 0 : oe[ae]) == null ? void 0 : Ne.call(oe, N, M.row.original);
4853
+ const K = (Ne = (oe = i == null ? void 0 : i.columnLinks) == null ? void 0 : oe[ae]) == null ? void 0 : Ne.call(oe, N, q.row.original);
4854
4854
  return K && N ? /* @__PURE__ */ t(
4855
4855
  Ce,
4856
4856
  {
@@ -4882,7 +4882,7 @@ function gs() {
4882
4882
  filters: _,
4883
4883
  onFiltersChange: (U) => m({ ...U, page: null }),
4884
4884
  onSubmit: (U, ae) => {
4885
- const De = f === 0 && U === $ && Object.entries(ae).every(([A, M]) => (_[A] ?? "") === (M || ""));
4885
+ const De = f === 0 && U === $ && Object.entries(ae).every(([A, q]) => (_[A] ?? "") === (q || ""));
4886
4886
  m({ q: U || null, ...ae, page: null }), De && L();
4887
4887
  },
4888
4888
  loadValues: (U, ae) => O.masterFilterValues(e, U, ae, 12).then((De) => De.values)
@@ -4909,7 +4909,7 @@ function gs() {
4909
4909
  Te && /* @__PURE__ */ r("div", { className: "absolute right-0 z-20 mt-2 max-h-80 w-60 overflow-y-auto rounded-lg border border-line bg-surface p-2 shadow-card", children: [
4910
4910
  /* @__PURE__ */ t("div", { className: "px-2 py-1 text-xs font-medium uppercase tracking-wide text-fg-muted", children: s("common.columns", { defaultValue: "Columns" }) }),
4911
4911
  _e.map((U) => {
4912
- var A, M;
4912
+ var A, q;
4913
4913
  const ae = ne.includes(U), De = ae && ne.length === 1;
4914
4914
  return /* @__PURE__ */ r(
4915
4915
  "label",
@@ -4923,11 +4923,11 @@ function gs() {
4923
4923
  className: "h-4 w-4 rounded border-line text-brand focus:ring-brand/30 disabled:opacity-50",
4924
4924
  checked: ae,
4925
4925
  disabled: De,
4926
- onChange: () => qe(U)
4926
+ onChange: () => Me(U)
4927
4927
  }
4928
4928
  ),
4929
4929
  s(`fields.${((A = i == null ? void 0 : i.fields.find((N) => N.name === U)) == null ? void 0 : A.label) ?? Pt(U)}`, {
4930
- defaultValue: ((M = i == null ? void 0 : i.fields.find((N) => N.name === U)) == null ? void 0 : M.label) ?? Pt(U)
4930
+ defaultValue: ((q = i == null ? void 0 : i.fields.find((N) => N.name === U)) == null ? void 0 : q.label) ?? Pt(U)
4931
4931
  })
4932
4932
  ]
4933
4933
  },
@@ -5006,7 +5006,7 @@ function gs() {
5006
5006
  function fs({ company: e, value: n, onChange: a }) {
5007
5007
  const { t: i } = ie(), o = ye({
5008
5008
  queryKey: ["company-sales-tax-templates", e],
5009
- queryFn: () => q(
5009
+ queryFn: () => M(
5010
5010
  `/masters/company/${encodeURIComponent(e)}/sales-tax-templates`
5011
5011
  ),
5012
5012
  enabled: !!e
@@ -5186,7 +5186,7 @@ function pa() {
5186
5186
  ] }, d.name);
5187
5187
  if (d.type === "link" && d.linkDoctype)
5188
5188
  return /* @__PURE__ */ t(
5189
- Me,
5189
+ qe,
5190
5190
  {
5191
5191
  label: f(d),
5192
5192
  value: m[d.name] ?? "",
@@ -5324,7 +5324,7 @@ function As() {
5324
5324
  return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
5325
5325
  /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-4", children: [
5326
5326
  /* @__PURE__ */ t(
5327
- Me,
5327
+ qe,
5328
5328
  {
5329
5329
  label: e("fields.Company", { defaultValue: "Company" }),
5330
5330
  value: s,
@@ -5498,7 +5498,7 @@ function Rs() {
5498
5498
  return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
5499
5499
  /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-4", children: [
5500
5500
  /* @__PURE__ */ t(
5501
- Me,
5501
+ qe,
5502
5502
  {
5503
5503
  label: e("fields.Account", { defaultValue: "Account" }),
5504
5504
  value: m,
@@ -5508,7 +5508,7 @@ function Rs() {
5508
5508
  }
5509
5509
  ),
5510
5510
  /* @__PURE__ */ t(
5511
- Me,
5511
+ qe,
5512
5512
  {
5513
5513
  label: e("fields.Party", { defaultValue: "Party" }),
5514
5514
  value: x,
@@ -5645,7 +5645,7 @@ function Bs() {
5645
5645
  return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
5646
5646
  /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-4", children: [
5647
5647
  /* @__PURE__ */ t(
5648
- Me,
5648
+ qe,
5649
5649
  {
5650
5650
  label: e("fields.Item Code", { defaultValue: "Item Code" }),
5651
5651
  value: o,
@@ -5655,7 +5655,7 @@ function Bs() {
5655
5655
  }
5656
5656
  ),
5657
5657
  /* @__PURE__ */ t(
5658
- Me,
5658
+ qe,
5659
5659
  {
5660
5660
  label: e("fields.Warehouse", { defaultValue: "Warehouse" }),
5661
5661
  value: l,
@@ -5701,7 +5701,7 @@ function Is() {
5701
5701
  }, k = b && (b.income && b.income.length > 0 || b.expense && b.expense.length > 0);
5702
5702
  return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
5703
5703
  /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-4", children: [
5704
- /* @__PURE__ */ t(Me, { label: e("fields.Company", { defaultValue: "Company" }), value: s, onChange: l, linkDoctype: "company", readOnly: !1 }),
5704
+ /* @__PURE__ */ t(qe, { label: e("fields.Company", { defaultValue: "Company" }), value: s, onChange: l, linkDoctype: "company", readOnly: !1 }),
5705
5705
  /* @__PURE__ */ t(me, { label: e("fields.From Date", { defaultValue: "From Date" }), type: "date", value: u, onChange: (h) => f(h.target.value) }),
5706
5706
  /* @__PURE__ */ t(me, { label: e("fields.To Date", { defaultValue: "To Date" }), type: "date", value: m, onChange: (h) => v(h.target.value) }),
5707
5707
  /* @__PURE__ */ t(W, { onClick: C, children: e("common.apply") })
@@ -5785,7 +5785,7 @@ function Es() {
5785
5785
  }, g = m && (((C = m.assets) == null ? void 0 : C.length) > 0 || ((k = m.liabilities) == null ? void 0 : k.length) > 0 || ((h = m.equity) == null ? void 0 : h.length) > 0), S = m ? Math.abs(m.total_assets - m.total_liabilities_and_equity) < 0.01 : !1;
5786
5786
  return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
5787
5787
  /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-4", children: [
5788
- /* @__PURE__ */ t(Me, { label: e("fields.Company", { defaultValue: "Company" }), value: o, onChange: s, linkDoctype: "company", readOnly: !1 }),
5788
+ /* @__PURE__ */ t(qe, { label: e("fields.Company", { defaultValue: "Company" }), value: o, onChange: s, linkDoctype: "company", readOnly: !1 }),
5789
5789
  /* @__PURE__ */ t(me, { label: e("fields.As of Date", { defaultValue: "As of Date" }), type: "date", value: l, onChange: (y) => c(y.target.value) }),
5790
5790
  /* @__PURE__ */ t(W, { onClick: b, children: e("common.apply") })
5791
5791
  ] }),
@@ -5826,7 +5826,7 @@ function Ls() {
5826
5826
  };
5827
5827
  return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
5828
5828
  /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-4", children: [
5829
- /* @__PURE__ */ t(Me, { label: e("fields.Company", { defaultValue: "Company" }), value: o, onChange: s, linkDoctype: "company", readOnly: !1 }),
5829
+ /* @__PURE__ */ t(qe, { label: e("fields.Company", { defaultValue: "Company" }), value: o, onChange: s, linkDoctype: "company", readOnly: !1 }),
5830
5830
  /* @__PURE__ */ t(me, { label: e("fields.As of Date", { defaultValue: "As of Date" }), type: "date", value: l, onChange: (g) => c(g.target.value) }),
5831
5831
  /* @__PURE__ */ t(W, { onClick: b, children: e("common.apply") })
5832
5832
  ] }),
@@ -5886,7 +5886,7 @@ function Os() {
5886
5886
  };
5887
5887
  return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
5888
5888
  /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-4", children: [
5889
- /* @__PURE__ */ t(Me, { label: e("fields.Company", { defaultValue: "Company" }), value: o, onChange: s, linkDoctype: "company", readOnly: !1 }),
5889
+ /* @__PURE__ */ t(qe, { label: e("fields.Company", { defaultValue: "Company" }), value: o, onChange: s, linkDoctype: "company", readOnly: !1 }),
5890
5890
  /* @__PURE__ */ t(me, { label: e("fields.As of Date", { defaultValue: "As of Date" }), type: "date", value: l, onChange: (g) => c(g.target.value) }),
5891
5891
  /* @__PURE__ */ t(W, { onClick: b, children: e("common.apply") })
5892
5892
  ] }),
@@ -5952,10 +5952,10 @@ function fr(e, n, a) {
5952
5952
  const i = a ? e.filter((o) => o[a] != null).map((o) => Fs(o[a])) : [];
5953
5953
  return i.length ? n === "sum" ? i.reduce((o, s) => o + s, 0) : n === "avg" ? i.reduce((o, s) => o + s, 0) / i.length : n === "min" ? Math.min(...i) : Math.max(...i) : 0;
5954
5954
  }
5955
- function qs(e, n) {
5955
+ function Ms(e, n) {
5956
5956
  return e == null && n == null ? 0 : e == null ? 1 : n == null ? -1 : typeof e == "number" && typeof n == "number" ? e - n : String(e).localeCompare(String(n), void 0, { numeric: !0 });
5957
5957
  }
5958
- function Ms(e, n) {
5958
+ function qs(e, n) {
5959
5959
  const a = n.get(e.source);
5960
5960
  if (!a) throw new Error(`Unknown report source: ${e.source}`);
5961
5961
  const i = e.dimensions ?? [], o = e.measures ?? [];
@@ -5982,7 +5982,7 @@ function Ms(e, n) {
5982
5982
  }
5983
5983
  for (const l of [...e.sort ?? []].reverse()) {
5984
5984
  const c = l.direction === "desc" ? -1 : 1;
5985
- s.sort((p, u) => qs(p[l.field], u[l.field]) * c);
5985
+ s.sort((p, u) => Ms(p[l.field], u[l.field]) * c);
5986
5986
  }
5987
5987
  return e.limit && (s = s.slice(0, e.limit)), {
5988
5988
  id: e.id,
@@ -5996,7 +5996,7 @@ function $s(e, n) {
5996
5996
  throw new Error("Unsupported custom report definition. Create a new version 1 report.");
5997
5997
  const a = new Map(n.map((l) => [l.name, l.rows])), i = e.report.tables.map((l) => {
5998
5998
  if (a.has(l.id)) throw new Error(`Duplicate report source/table id: ${l.id}`);
5999
- const c = Ms(l, a);
5999
+ const c = qs(l, a);
6000
6000
  return a.set(l.id, c.rows), c;
6001
6001
  }), o = new Set(i.map((l) => l.id)), s = (e.report.charts ?? []).map((l) => {
6002
6002
  if (!o.has(l.data_table))
@@ -6596,8 +6596,8 @@ function no() {
6596
6596
  }, D = (d) => s((L) => pn(L, d)), V = a * un;
6597
6597
  return /* @__PURE__ */ r("div", { className: "space-y-4", children: [
6598
6598
  /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-3", children: [
6599
- /* @__PURE__ */ t(Me, { label: e("rentals.yard", { defaultValue: "Yard" }), value: l, onChange: c, linkDoctype: "warehouse", readOnly: !1 }),
6600
- /* @__PURE__ */ t(Me, { label: e("rentals.machineType", { defaultValue: "Machine type" }), value: p, onChange: u, linkDoctype: "item", readOnly: !1 }),
6599
+ /* @__PURE__ */ t(qe, { label: e("rentals.yard", { defaultValue: "Yard" }), value: l, onChange: c, linkDoctype: "warehouse", readOnly: !1 }),
6600
+ /* @__PURE__ */ t(qe, { label: e("rentals.machineType", { defaultValue: "Machine type" }), value: p, onChange: u, linkDoctype: "item", readOnly: !1 }),
6601
6601
  /* @__PURE__ */ t(W, { onClick: _, children: e("common.apply", { defaultValue: "Apply" }) }),
6602
6602
  /* @__PURE__ */ r("div", { className: "ml-auto flex items-center gap-2", children: [
6603
6603
  /* @__PURE__ */ r(W, { variant: "secondary", onClick: () => D(-a), children: [
@@ -7435,7 +7435,7 @@ function va(e) {
7435
7435
  });
7436
7436
  }
7437
7437
  function xa() {
7438
- var qn;
7438
+ var Mn;
7439
7439
  const { t: e } = ie(), { sessionId: n } = bt(), a = Ue(), i = Ct(), [o] = Et(), s = o.get("demo") === "1", [l, c] = w(""), [p, u] = w(""), [f, m] = w([]), [v, x] = w(""), [b, g] = w(!1), [S, C] = w("idle"), k = pe(null), h = pe(null), y = pe([]), {
7440
7440
  sessions: R,
7441
7441
  connectionStatus: E,
@@ -7453,9 +7453,9 @@ function xa() {
7453
7453
  isThinking: P,
7454
7454
  getDemoStatus: j,
7455
7455
  getDemoTypingCue: se
7456
- } = vt(), ue = pe(null), we = pe(null), ve = pe(null), _e = pe(null), [B, re] = w(0), be = pe(!1), z = pe(null), ne = pe(0), qe = pe(!0), Te = pe(null), je = pe(!1), We = pe(null), Je = d(n), it = (qn = R.find((I) => I.id === n)) == null ? void 0 : qn.title;
7456
+ } = vt(), ue = pe(null), we = pe(null), ve = pe(null), _e = pe(null), [B, re] = w(0), be = pe(!1), z = pe(null), ne = pe(0), Me = pe(!0), Te = pe(null), je = pe(!1), We = pe(null), Je = d(n), it = (Mn = R.find((I) => I.id === n)) == null ? void 0 : Mn.title;
7457
7457
  ze(it || null);
7458
- const Ie = E === "connected", U = P(n), ae = j(n), De = se(n), A = s || ae === "running", M = L(n), N = F(n), T = Je[Je.length - 1], K = T ? `${T.type}:${T.timestamp || ""}:${T.content}` : "empty", oe = Ve(() => {
7458
+ const Ie = E === "connected", U = P(n), ae = j(n), De = se(n), A = s || ae === "running", q = L(n), N = F(n), T = Je[Je.length - 1], K = T ? `${T.type}:${T.timestamp || ""}:${T.content}` : "empty", oe = Ve(() => {
7459
7459
  je.current = !0, We.current !== null && window.clearTimeout(We.current), We.current = window.setTimeout(() => {
7460
7460
  je.current = !1, We.current = null;
7461
7461
  }, 600);
@@ -7465,23 +7465,23 @@ function xa() {
7465
7465
  oe(), te.scrollTo({ top: te.scrollHeight, behavior: I });
7466
7466
  const ge = () => {
7467
7467
  const Re = ue.current;
7468
- Re && (!qe.current && !A && !U || Re.scrollTop + Re.clientHeight >= Re.scrollHeight - 2 || (oe(), Re.scrollTo({ top: Re.scrollHeight, behavior: "auto" })));
7468
+ Re && (!Me.current && !A && !U || Re.scrollTop + Re.clientHeight >= Re.scrollHeight - 2 || (oe(), Re.scrollTo({ top: Re.scrollHeight, behavior: "auto" })));
7469
7469
  };
7470
7470
  requestAnimationFrame(ge), window.setTimeout(ge, 120);
7471
7471
  }, [A, U, oe]);
7472
- Mn(() => {
7472
+ qn(() => {
7473
7473
  const I = ue.current, te = Te.current;
7474
7474
  if (!I || te === null) return;
7475
7475
  const ge = I.scrollHeight - te;
7476
7476
  ge > 0 && (I.scrollTop = I.scrollTop + ge), Te.current = null;
7477
7477
  }, [Je.length]), ce(() => {
7478
- Te.current === null && (!qe.current && !A && !U || Ne(A ? "auto" : "smooth"));
7478
+ Te.current === null && (!Me.current && !A && !U || Ne(A ? "auto" : "smooth"));
7479
7479
  }, [K, A, U, Ne]), ce(() => {
7480
7480
  const I = ue.current;
7481
7481
  if (!I || typeof ResizeObserver > "u") return;
7482
7482
  let te = 0;
7483
7483
  const ge = new ResizeObserver(() => {
7484
- Te.current === null && (!qe.current && !A && !U || (cancelAnimationFrame(te), te = requestAnimationFrame(() => {
7484
+ Te.current === null && (!Me.current && !A && !U || (cancelAnimationFrame(te), te = requestAnimationFrame(() => {
7485
7485
  const Be = ue.current;
7486
7486
  if (!Be) return;
7487
7487
  Be.scrollHeight - Be.scrollTop - Be.clientHeight <= 120 && (oe(), Be.scrollTo({ top: Be.scrollHeight, behavior: "auto" }));
@@ -7490,19 +7490,19 @@ function xa() {
7490
7490
  return Re && ge.observe(Re), ge.observe(I), () => {
7491
7491
  ge.disconnect(), cancelAnimationFrame(te);
7492
7492
  };
7493
- }, [A, U, oe]), Mn(() => {
7493
+ }, [A, U, oe]), qn(() => {
7494
7494
  const I = _e.current;
7495
7495
  if (!I || (re(I.offsetHeight), typeof ResizeObserver > "u")) return;
7496
7496
  const te = new ResizeObserver(() => re(I.offsetHeight));
7497
7497
  return te.observe(I), () => te.disconnect();
7498
7498
  }, []);
7499
7499
  const Se = Ve(() => {
7500
- if (!n || N || !M) return;
7500
+ if (!n || N || !q) return;
7501
7501
  const I = ue.current;
7502
- Te.current = I ? I.scrollHeight : null, qe.current = !1, _(n).catch(() => {
7502
+ Te.current = I ? I.scrollHeight : null, Me.current = !1, _(n).catch(() => {
7503
7503
  Te.current = null;
7504
7504
  });
7505
- }, [n, N, M, _]), Ze = pe(null);
7505
+ }, [n, N, q, _]), Ze = pe(null);
7506
7506
  ce(() => {
7507
7507
  u(""), z.current = null, ne.current += 1, n && Za(n);
7508
7508
  const I = i.state;
@@ -7535,7 +7535,7 @@ function xa() {
7535
7535
  function gt(I) {
7536
7536
  if (je.current) return;
7537
7537
  const te = I.currentTarget, ge = te.scrollHeight - te.scrollTop - te.clientHeight;
7538
- qe.current = ge < 80;
7538
+ Me.current = ge < 80;
7539
7539
  }
7540
7540
  ce(() => {
7541
7541
  !n || !Ie || !s || z.current !== n && (z.current = n, u(""), H(n).catch((I) => {
@@ -7703,11 +7703,11 @@ function xa() {
7703
7703
  className: "mx-auto max-w-3xl space-y-4",
7704
7704
  style: { paddingBottom: (B || 96) + 16 },
7705
7705
  children: [
7706
- (M || N) && Je.length > 0 && /* @__PURE__ */ t("div", { className: "flex justify-center", children: /* @__PURE__ */ t(
7706
+ (q || N) && Je.length > 0 && /* @__PURE__ */ t("div", { className: "flex justify-center", children: /* @__PURE__ */ t(
7707
7707
  "button",
7708
7708
  {
7709
7709
  onClick: Se,
7710
- disabled: N || !M,
7710
+ disabled: N || !q,
7711
7711
  className: "rounded-full bg-surface px-3 py-1 text-xs text-fg-muted ring-1 ring-line transition-colors hover:bg-surface-subtle hover:text-fg disabled:cursor-not-allowed disabled:opacity-60",
7712
7712
  children: e(N ? "common.loading" : "chat.loadOlder")
7713
7713
  }
@@ -8112,7 +8112,7 @@ function wo(e, n) {
8112
8112
  }
8113
8113
  return i < e.length && a.push(e.slice(i)), a.length > 0 ? a : [e];
8114
8114
  }
8115
- const Co = { incomplete: "Draft saved; PDF output requires Customer, Company and at least one Quotation." }, So = { save: "Save", saving: "Saving...", submit: "Submit", cancel: "Cancel", cancelDoc: "Cancel", cancelDocAbort: "Don't cancel", cancelDocConfirmBtn: "Yes, cancel document", cancelBooking: "Cancel booking", cancelBookingConfirm: "Yes, cancel booking", keepBooking: "Keep booking", discardDraft: "Discard draft", keepDraft: "Keep", discardDraftConfirm: "Discard draft", showDiscarded: "Show discarded", showDisabled: "Show disabled", columns: "Columns", searchPlaceholder: "Search…", noRecords: "No records found", active: "Active", disabled: "Disabled", sortByColumn: "Sort by this column", delete: "Delete", edit: "Edit", pdf: "PDF", addRow: "Add Row", loading: "Loading...", enable: "Enable", disable: "Disable", refresh: "Refresh", refreshing: "Refreshing", areYouSure: "Are you sure?", all: "All", apply: "Apply", new: "New", prev: "Prev", next: "Next", perPage: "Per page", page: "Page", of: "of", showing: "Showing", total: "Total", totals: "Totals", errorOccurred: "An error occurred" }, Ao = { today: "Today", thisWeek: "This Week", thisMonth: "This Month", thisQuarter: "This Quarter", thisYear: "This Year", lastMonth: "Last Month", lastQuarter: "Last Quarter", lastYear: "Last Year", endOfThisMonth: "End of This Month", endOfLastMonth: "End of Last Month", endOfThisQuarter: "End of This Quarter", endOfThisYear: "End of This Year", endOfLastYear: "End of Last Year" }, _o = { title: "Bank statement import", subtitle: "Preview and import ISO 20022 CAMT statements without posting to the ledger.", selectFiles: "CAMT files", chooseFiles: "Choose XML or ZIP files", dropFiles: "Drop XML or ZIP files here", unsupportedFiles: "Only CAMT XML or ZIP files are supported: {{files}}", fileHint: "camt.053.001.04 and camt.053.001.08 · multiple files supported", preview: "Show preview", noPosting: "Previewing and importing do not post accounting entries.", alreadyImported: "Already imported", period: "Period", openingBalance: "Opening balance", closingBalance: "Closing balance", entries: "Entries", credits: "Credits", debits: "Debits", details: "Transaction details", batches: "batches", qrReferences: "QR references", mapping: "Bank account mapping", selectBankAccount: "Select a Bank Account…", noBankAccounts: "No Bank Account has been configured yet.", createBankAccount: "Create Bank Account", mappingRequired: "Map every new statement to a Bank Account before importing.", import: "Import bank statements", importComplete: "Import completed", importCounts: "{{imported}} entries imported, {{duplicates}} duplicates skipped", history: "Recent imports", importId: "Import", bankAccount: "Bank account", source: "Source file", openReconciliation: "Open bank reconciliation" }, Do = { title: "Bank reconciliation", subtitle: "Match imported bank transactions to posted vouchers, open invoices, or ledger accounts.", status: "Status", unreconciled: "Unreconciled", reconciled: "Reconciled", transactions: "Bank transactions", empty: "No bank transactions in this view.", remittance: "Remittance information", reference: "Reference", openTransaction: "Open bank transaction", conversionRate: "Exchange rate (1 {{currency}} in {{base}})", conversionRateHint: "Required when no exchange rate exists for the transaction date.", existingVouchers: "Existing accounting entries", existingVouchersHint: "These submitted vouchers contain an exact movement on this bank account. Matching does not post them again.", match: "Match", openInvoices: "Open invoices", openInvoicesHint: "Select one or more invoices belonging to the same party. Confirmation creates and submits a Payment Entry.", outstanding: "Outstanding", allocation: "Allocation", noInvoiceSuggestions: "No plausible open invoice was found.", allocated: "Allocated {{amount}} of {{total}}", createPayment: "Create payment and reconcile", directPosting: "Post to a ledger account", directPostingHint: "For fees, interest and other non-invoice movements. Receivable, payable, bank and cash control accounts are intentionally excluded.", counterpartyAccount: "Counter account", remarks: "Remarks", createJournal: "Create journal entry and reconcile", activeMatch: "Active reconciliation", undo: "Undo reconciliation", confirmExistingTitle: "Match existing entry?", confirmExistingBody: "Match {{transaction}} to {{voucher}}? No new ledger entry will be posted.", confirmGroupBody: "Match all {{count}} transactions totalling {{amount}} to {{voucher}}? No new ledger entry will be posted.", groupedTransactions: "{{count}} grouped transactions", confirmPaymentTitle: "Post payment and reconcile?", confirmPaymentBody: "This submits a payment of {{amount}} for {{transaction}} and allocates it across {{count}} invoice(s).", onAccountWarning: "{{amount}} will remain as an on-account payment for this party.", confirmJournalTitle: "Post journal entry and reconcile?", confirmJournalBody: "This posts {{amount}} for {{transaction}} against {{account}}.", postAndReconcile: "Post and reconcile", undoTitle: "Undo reconciliation?", undoCreatedBody: "The voucher created by this reconciliation will be cancelled, its ledger entries reversed, and invoice allocations restored.", undoExistingBody: "The existing voucher will only be unlinked. It remains submitted and its ledger entries are not changed.", undoExistingGroupBody: "All {{count}} grouped transactions ({{transactions}}) will be unlinked together. The existing voucher remains submitted and its ledger entries are not changed.", reconciledNotice: "Bank transaction reconciled.", groupReconciledNotice: "{{count}} bank transactions reconciled as one group.", undoneNotice: "Reconciliation reversed." }, Po = { placeholder: "Search or enter field:value…", search: "Search", addFilter: "+ Filter", availableFields: "Available fields", fieldHelp: "Choose a field or type its technical name followed by a colon.", field: "Field", valueFor: "Value for {{label}}", allFields: "All fields", typeForValues: "Start typing to find values in this field.", noValues: "No matching values found.", noFields: "No matching fields found.", useValue: "Use “{{value}}”", removeFilter: "Remove {{label}} filter", types: { text: "Text", textarea: "Long text", select: "Selection", link: "Linked record", date: "Date", datetime: "Date and time", number: "Number", currency: "Amount" } }, To = { title: "Language", help: "Choose the language for the interface. Your choice is saved in this browser.", label: "Language" }, Ro = { back: "Back", logout: "Logout", openMenu: "Open menu", closeMenu: "Close menu", new: "New {{label}}" }, Bo = { dashboard: "Dashboard", setup: "Company Setup", tutorial: "Getting Started", chat: "Chat", reports: "Reports", "trial-balance": "Trial Balance", "general-ledger": "General Ledger", "stock-balance": "Stock Balance", "profit-and-loss": "Profit & Loss", "balance-sheet": "Balance Sheet", "ar-aging": "Accounts Receivable Aging", "ap-aging": "Accounts Payable Aging", analytics: "Analytics", "chart-of-accounts": "Chart of Accounts" }, Io = { quotation: { one: "Quotation", other: "Quotations" }, proposal: { one: "Proposal", other: "Proposals" }, "sales-order": { one: "Sales Order", other: "Sales Orders" }, "sales-invoice": { one: "Sales Invoice", other: "Sales Invoices" }, "pos-invoice": { one: "POS Invoice", other: "POS Invoices" }, "purchase-order": { one: "Purchase Order", other: "Purchase Orders" }, "purchase-invoice": { one: "Purchase Invoice", other: "Purchase Invoices" }, "payment-entry": { one: "Payment Entry", other: "Payment Entries" }, "journal-entry": { one: "Journal Entry", other: "Journal Entries" }, "bank-transaction": { one: "Bank Transaction", other: "Bank Transactions" }, "bank-account": { one: "Bank Account", other: "Bank Accounts" }, budget: { one: "Budget", other: "Budgets" }, subscription: { one: "Subscription", other: "Subscriptions" }, "stock-entry": { one: "Stock Entry", other: "Stock Entries" }, "delivery-note": { one: "Delivery Note", other: "Delivery Notes" }, "purchase-receipt": { one: "Purchase Receipt", other: "Purchase Receipts" }, "pricing-rule": { one: "Pricing Rule", other: "Pricing Rules" } }, Eo = { company: { one: "Company", other: "Companies" }, customer: { one: "Customer", other: "Customers" }, supplier: { one: "Supplier", other: "Suppliers" }, item: { one: "Item", other: "Items" }, warehouse: { one: "Warehouse", other: "Warehouses" } }, Lo = { chats: "Chats", newChat: "New Chat", deleteChat: "Delete chat", customAnalytics: "Custom Analytics", newAnalytics: "New Analytics", deleteReport: "Delete report", groups: { Introduction: "Introduction", Selling: "Selling", Buying: "Buying", Accounting: "Accounting", Stock: "Stock", Reports: "Reports", Masters: "Masters", Settings: "Settings", Rentals: "Rentals" }, items: { "Getting Started": "Getting Started", "Company Setup": "Company Setup", "Opening Balances": "Opening Balances", Quotation: "Quotation", Proposal: "Proposals", "Sales Order": "Sales Order", "Sales Invoice": "Sales Invoice", "POS Invoice": "POS Invoice", "Purchase Order": "Purchase Order", "Purchase Invoice": "Purchase Invoice", "Payment Entry": "Payment Entry", "Journal Entry": "Journal Entry", "Bank Transaction": "Bank Transaction", "Bank Account": "Bank Accounts", "Bank Statement Import": "Bank Statement Import", "Bank Reconciliation": "Bank Reconciliation", Budget: "Budget", Subscription: "Subscription", "Stock Entry": "Stock Entry", "Delivery Note": "Delivery Note", "Purchase Receipt": "Purchase Receipt", "Trial Balance": "Trial Balance", "Profit & Loss": "Profit & Loss", "Balance Sheet": "Balance Sheet", "General Ledger": "General Ledger", "AR Aging": "AR Aging", "AP Aging": "AP Aging", Analytics: "Analytics", "Stock Balance": "Stock Balance", Company: "Company", Customer: "Customer", Supplier: "Supplier", Item: "Item", Warehouse: "Warehouse", General: "General", "Pricing Rule": "Pricing Rule", "Users & Team": "Users & Team", "Chart of Accounts": "Chart of Accounts", "Fleet Calendar": "Fleet Calendar", "Fleet (Assets)": "Fleet (Assets)", Reservations: "Reservations" } }, Oo = { taglineFirstRun: "Create your admin account to get started", taglineRegister: "Create your account", taglineSignIn: "Sign in to your account", demoBanner: "Demo mode is active. Sign in with an admin account to manage settings or disable public access.", fullName: "Full Name", email: "Email", password: "Password", confirmPassword: "Confirm Password", createAdmin: "Create Admin Account", register: "Register", signIn: "Sign In", browsingTitle: "Just browsing?", browsingBody: "Start a fresh chat session and watch the live demo.", enterDemo: "Enter Live Demo", haveInvite: "Have an invite?", noAccountPrompt: "Need an account?", registerLink: "Register", alreadyHaveAccount: "Already have an account?", signInLink: "Sign in", passwordsNoMatch: "Passwords do not match", passwordTooShort: "Password must be at least 6 characters", registrationFailed: "Registration failed", loginFailed: "Login failed", orContinueWith: "or continue with", continueWithProvider: "Continue with {{provider}}", oauthError: { generic: "Sign-in failed. Please try again.", email_exists: "An account with this email already exists. Sign in, then link this provider from your account settings.", email_unverified: "Your provider did not confirm a verified email, so we can't sign you in.", registration_closed: "This email has no account yet and registration is invite-only.", invite_invalid: "That invite is invalid, already used, or issued for a different email.", identity_taken: "That provider account is already linked to a different user.", account_disabled: "This account has been disabled.", token_exchange_failed: "Sign-in failed while contacting the provider. Please try again." } }, Fo = { pdfTitle: "PDF & Print", pageSize: "Page Size", pdfHelp: "A4 is standard internationally (210 x 297 mm). Letter is standard in the US (8.5 x 11 in).", passwordTitle: "Change Password", currentPassword: "Current password", newPassword: "New password", confirmNewPassword: "Confirm new password", changePassword: "Change password", changingPassword: "Changing...", passwordChanged: "Password changed.", setPasswordTitle: "Set a Password", setPasswordBody: "Your account signs in with Google. Set a password to also sign in with email and password — a handy fallback if you ever lose access to your Google account.", setPassword: "Set password", passwordSet: "Password set.", linkedAccountsTitle: "Linked Accounts", linkedAccountsBody: "Link a Google or Apple account to sign in without a password.", linked: "Linked", linkProvider: "Link {{provider}}", openingTitle: "Opening Balances", openingBody: "The Opening Balances page allows importing account balances, stock, and outstanding invoices from a previous system.", openingEnabled: "Currently enabled — accessible under Introduction > Opening Balances.", openingDisabled: "Currently disabled — the page is hidden from the sidebar.", signupTitle: "Public Signup", signupBody: "Allow anyone to create their own account (as a viewer) without an invite. When off, new users can only join via an invite link.", signupEnabled: "Currently enabled — anyone with the URL can register as a viewer.", signupDisabled: "Currently disabled — registration is invite-only.", chatApiTitle: "Chat API", apiTitle: "Programmatic API", restApiBody: "Allow apps and scripts to access this ERP through REST or MCP, using API keys or OAuth account linking. Disabled by default.", restApiEnabled: "Currently enabled — issued keys can call /api/documents, /api/masters, and the rest of the REST API.", restApiDisabled: "Currently disabled — the REST API only accepts the session cookie; keys are rejected.", restApiDeactivateTitle: "Deactivate the REST API?", restApiDeactivateBody: "Bearer-key access to the REST API will be switched off — connectors and scripts are rejected until you turn it back on. The web app (session cookie) is unaffected, and your API keys are NOT deleted: they start working again the instant you re-enable the API.", chatApiBody: "Let external applications talk to this ERP's chat agent over an HTTP API, using Bearer API keys. Off by default.", chatApiEnabled: "Currently enabled — issued keys can call POST /api/v1/chat.", chatApiDisabled: "Currently disabled — the API returns 404 and keys don't work.", chatApiDeactivateTitle: "Deactivate the chat API?", chatApiDeactivateBody: "The chat API will be switched off — every programmatic request (Bearer API keys) is rejected until you turn it back on. Your API keys are NOT deleted: they stay saved and start working again the instant you re-enable the API.", chatApiDeactivateConfirm: "Deactivate API", chatApiKeysTitle: "Apps and API keys", chatApiTokenOnce: "Copy this token now — it won't be shown again.", chatApiCopy: "Copy", chatApiDismiss: "Dismiss", chatApiRevoked: "revoked", chatApiRevoke: "Revoke", chatApiNoKeys: "No connections yet.", chatApiName: "Name", chatApiRole: "Role", chatApiCreate: "Create key", chatApiWarn: "A key acts with your identity and at most your role — treat it like a password. Revoke first, then delete.", publicTitle: "Public Access (Demo Mode)", publicActive: "Active", publicActiveBody: "Public access is enabled. Anyone can use the application without logging in. All visitors get manager-level permissions.", disablePublic: "Disable Public Access", disabling: "Disabling...", publicInactiveBody: "Enable public access to let anyone use the application without an account. Useful for demos and showcases. All visitors get manager-level permissions and share the same identity.", enablePublic: "Enable Public Access", enableWarning: "This will allow anyone to access the application without logging in. They will be able to create, edit, and submit documents. Your admin account still requires login.", yesEnable: "Yes, Enable", enabling: "Enabling...", tokenSpendTitle: "Token Spend", chatApiDelete: "Delete", chatApiYourKeys: "Your connections", chatApiYouBadge: "you", chatApiUnknownUser: "Unknown user", chatApiAdminAllNote: "As an administrator, you can manage everyone’s connections. Your own connections appear first.", chatApiConfirmTitle: "This connection belongs to another user", chatApiConfirmRevokeBody: "“{{key}}” belongs to {{name}}. Revoking it will immediately disconnect their app. Continue?", chatApiConfirmDeleteBody: "“{{key}}” belongs to {{name}}. Permanently remove this revoked connection?", chatApiPersonal: "Connect apps using your ERP account. Access never exceeds your current permissions.", version: "Version {{version}}" }, zo = { Draft: "Draft", Open: "Open", Submitted: "Submitted", "To Deliver and Bill": "To Deliver and Bill", "To Deliver": "To Deliver", "To Bill": "To Bill", Completed: "Completed", Ordered: "Ordered", Paid: "Paid", Unpaid: "Unpaid", Cancelled: "Cancelled", Discarded: "Discarded", Overdue: "Overdue", Return: "Return", Closed: "Closed" }, qo = { periodRange: "Reporting period: {{from}} – {{to}}", periodFrom: "Reporting period from {{from}}", periodThrough: "Reporting period through {{to}}", asOfDate: "As of: {{date}}", balanced: "BALANCED", imbalanced: "IMBALANCED", income: "Income", expenses: "Expenses", totalIncome: "Total Income", totalExpenses: "Total Expenses", netProfit: "Net Profit", netLoss: "Net Loss", assets: "Assets", liabilities: "Liabilities", equity: "Equity", totalAssets: "Total Assets", totalLiabilities: "Total Liabilities", totalEquity: "Total Equity", totalLiabilitiesEquity: "Total Liabilities + Equity", current: "Current", noData: "No data found", noEntries: "No entries found", noStockData: "No stock data found", noReceivables: "No outstanding receivables", noPayables: "No outstanding payables", noDocuments: "No documents found" }, Mo = { required: "Required: {{fields}}", deleteConfirm: "Delete this {{label}}?", disabledNotice: "This {{label}} is disabled. It remains in the system because other records still reference it.", disabledOnDelete: "{{label}} {{name}} was disabled instead of deleted because it is referenced by {{reason}}." }, $o = { totalRevenue: "Total Revenue", outstandingReceivable: "Outstanding Receivable", outstandingPayable: "Outstanding Payable", totalStockValue: "Total Stock Value", recentDocuments: "Recent Documents", noRecentDocuments: "No recent documents" }, Vo = { title: "Lambda ERP Chat", subtitle: "Ask me to create documents, look up data, or run reports.", s1: "What customers do we have?", s2: "Show me the trial balance", s3: "Create a quotation for 10 Bolt Pack M8", s4: "List all unpaid invoices", loadOlder: "Load older messages", uploading: "Uploading...", failed: "Failed", remove: "Remove", placeholderDemo: "Running live demo...", placeholderType: "Type a message...", placeholderConnecting: "Connecting...", attachTitle: "Attach file (PDF or image, max 10 MB)", creating: "Creating chat...", dropToAttach: "Drop file to attach (images or PDF, max 10 MB each)", demoError: "Could not start demo", voiceStart: "Record voice message", voiceStop: "Stop recording", voiceTranscribing: "Transcribing...", micDenied: "Microphone access was denied.", transcribeFailed: "Transcription failed. Please try again." }, Uo = { "Create Credit Note": "Create Credit Note", "Create Debit Note": "Create Debit Note", "Create Delivery Note": "Create Delivery Note", "Create Purchase Invoice": "Create Purchase Invoice", "Create Purchase Receipt": "Create Purchase Receipt", "Create Return": "Create Return", "Create Sales Invoice": "Create Sales Invoice", "Create Sales Order": "Create Sales Order" }, Ko = { "Last modified": "Last modified", Created: "Created", "% Billed": "% Billed", "% Delivered": "% Delivered", Account: "Account", Address: "Address", Allocated: "Allocated", Amount: "Amount", "As of Date": "As of Date", Balance: "Balance", "Bank Account": "Bank Account", "Billing Interval": "Billing Interval", "Budget Amount": "Budget Amount", Buying: "Buying", Change: "Change", City: "City", Company: "Company", "Company Name": "Company Name", "Cost Center": "Cost Center", Country: "Country", Credit: "Credit", "Credit Limit": "Credit Limit", Currency: "Currency", "Current Period End": "Current Period End", Website: "Website", "Contact Person": "Contact Person", "Contact Email": "Contact Email", "Contact Phone": "Contact Phone", "Current Period Start": "Current Period Start", Customer: "Customer", "Customer Group": "Customer Group", "Customer Name": "Customer Name", Date: "Date", Debit: "Debit", "Delivery Date": "Delivery Date", Deposit: "Deposit", Description: "Description", "Discount %": "Discount %", "Discount Amt": "Discount Amt", "Discount Percentage": "Discount Percentage", "Due Date": "Due Date", Email: "Email", Enabled: "Enabled", "End Date": "End Date", "Expense Account": "Expense Account", "Fiscal Year": "Fiscal Year", "From Date": "From Date", "Grand Total": "Grand Total", Frequency: "Frequency", ID: "ID", "If Exceeded": "If Exceeded", "Income Account": "Income Account", "Incoming Value": "Incoming Value", Invoice: "Invoice", Item: "Item", "Item Code": "Item Code", "Item Group": "Item Group", "Item Name": "Item Name", "Matched Doc": "Matched Doc", "Matched Type": "Matched Type", "Min Qty": "Min Qty", Mode: "Mode", Month: "Month", Name: "Name", "Net Total": "Net Total", "Notes / Terms": "Notes / Terms", "Outgoing Value": "Outgoing Value", Outstanding: "Outstanding", "Outstanding Amount": "Outstanding Amount", "Paid Amount": "Paid Amount", "Paid From": "Paid From", "Paid To": "Paid To", Party: "Party", "Party Type": "Party Type", "Payment Type": "Payment Type", Phone: "Phone", "Plan Items": "Plan Items", "Posting Date": "Posting Date", Priority: "Priority", Qty: "Qty", Rate: "Rate", "Rate (%)": "Rate (%)", "Rate Or Discount": "Rate Or Discount", "Reference No": "Reference No", Remark: "Remark", Selling: "Selling", Source: "Source", "Source Warehouse": "Source Warehouse", "Standard Rate": "Standard Rate", "Start Date": "Start Date", Status: "Status", "Stock Entry Type": "Stock Entry Type", "Stock UOM": "Stock UOM", "Stock Value": "Stock Value", Supplier: "Supplier", "Supplier Group": "Supplier Group", "Supplier Name": "Supplier Name", Target: "Target", "Target Warehouse": "Target Warehouse", Tax: "Tax", "Tax ID": "Tax ID", Territory: "Territory", Title: "Title", "To Date": "To Date", Total: "Total", "Total Amount": "Total Amount", "Total Credit": "Total Credit", "Total Debit": "Total Debit", "Transaction Date": "Transaction Date", Type: "Type", Unallocated: "Unallocated", "Update Stock": "Update Stock", "Valid From": "Valid From", "Valid Till": "Valid Till", "Valid Upto": "Valid Upto", "Valuation Rate": "Valuation Rate", "Voucher No": "Voucher No", "Voucher Type": "Voucher Type", Warehouse: "Warehouse", "Warehouse Name": "Warehouse Name", Withdrawal: "Withdrawal", "Zip Code": "Zip Code", "Item (type)": "Item (type)", "Asset Tag / Plate": "Asset Tag / Plate", "Home Yard": "Home Yard", "Purchase Value": "Purchase Value", "Meter Reading": "Meter Reading", "Meter Unit": "Meter Unit", Yard: "Yard", "Asset (specific unit)": "Asset (specific unit)", Allocation: "Allocation", From: "From", To: "To", Purpose: "Purpose", "Purchase Date": "Purchase Date", Notes: "Notes" }, jo = { accounts: "accounts", items: "items", monthly_distribution: "monthly_distribution", payments: "payments", plans: "plans", references: "references", taxes: "taxes" }, Wo = /* @__PURE__ */ JSON.parse(`{"chips":{"Cancelled":"Cancelled","Credit Note":"Credit Note","DN Return":"DN Return","Debit Note":"Debit Note","Delivery Note":"Delivery Note","Draft":"Draft","Payment Entry":"Payment Entry","Purchase Invoice":"Purchase Invoice","Purchase Order":"Purchase Order","Purchase Receipt":"Purchase Receipt","Quotation":"Quotation","Sales Invoice":"Sales Invoice","Sales Order":"Sales Order","Submitted":"Submitted"},"links":{"AP Aging":"AP Aging","AR Aging":"AR Aging","Balance Sheet":"Balance Sheet","Customers":"Customers","Delivery Notes":"Delivery Notes","General Ledger":"General Ledger","Go to Setup":"Go to Setup","Items":"Items","New Journal Entry":"New Journal Entry","New Payment Entry":"New Payment Entry","New Purchase Order":"New Purchase Order","New Quotation":"New Quotation","New Stock Entry":"New Stock Entry","Open AI Chat":"Open AI Chat","Profit & Loss":"Profit & Loss","Purchase Invoices":"Purchase Invoices","Purchase Receipts":"Purchase Receipts","Sales Invoices":"Sales Invoices","Stock Balance":"Stock Balance","Suppliers":"Suppliers","Trial Balance":"Trial Balance","View Quotations":"View Quotations","Warehouses":"Warehouses"},"steps":{"1":{"title":"Set Up Your Company","description":"Create a company with a base currency. This generates your Chart of Accounts (30 standard accounts across Assets, Liabilities, Equity, Income, and Expenses) and a default Cost Center. You can also seed demo data with sample customers, suppliers, and items to get started quickly."},"2":{"title":"Create Master Data","description":"Before you can transact, you need master records. Create at least one Customer, one Supplier, one Item (with a standard rate), and one Warehouse. If you seeded demo data in Step 1, these already exist.","tip":"Items have a standard rate that auto-fills when you add them to documents. You can always override the rate per transaction."},"3":{"title":"Create a Quotation","description":"A Quotation is a non-binding offer to a customer. Select a customer, add line items with quantities and rates, optionally add tax rows, then save. Quotations have no financial impact — they don't create accounting or stock entries. Set a validity date so the offer expires automatically."},"4":{"title":"Submit and Convert to Sales Order","description":"Open your saved Quotation and click Submit to confirm it. Then click “Create Sales Order” to convert it. The Sales Order represents a confirmed commitment from the customer. It still has no financial impact, but it reserves stock for planning purposes.","tip":"Only submitted documents can be converted to the next step. Draft → Submit → Convert is the standard flow. For quick deals, you can skip the Sales Order and go directly from Quotation to Sales Invoice or Delivery Note."},"5":{"title":"Check Stock Before Fulfilling","description":"Before you can deliver, check if you actually have the item in stock. Go to the Stock Balance report and look up the item. If your warehouse has zero quantity, you’ll need to bring stock in first — either through a Purchase Order (buying from a supplier) or a Stock Entry (manual receipt).","tip":"This is a common real-world scenario: you sell something, then realize you need to buy it first. The ERP handles both flows."},"6":{"title":"Purchase Cycle: Buy Stock from a Supplier","description":"If you need to buy stock, you have two valid paths. The standard path is Purchase Order -> Purchase Receipt -> Purchase Invoice: use this when goods arrive before or separately from the supplier bill. If the bill and receipt happen together, you can create the Purchase Invoice directly and enable Update Stock so the same document both receives inventory and records Accounts Payable.","tip":"Use Purchase Receipt first when receiving goods separately. Use Purchase Invoice with Update Stock when one step should both receive stock and book the supplier bill. In that direct path, set a warehouse on each stock item row."},"7":{"title":"Create a Delivery Note","description":"Now that you have stock, go back to your submitted Sales Order and click “Create Delivery Note.” Set the warehouse on each item row (where the goods ship from), then submit. This moves inventory out of the warehouse — your stock balance decreases.","tip":"The Delivery Note is the shipping document. It reduces stock but doesn’t create an invoice. You can deliver and invoice separately."},"8":{"title":"Create and Submit the Sales Invoice","description":"From the submitted Sales Order, create a Sales Invoice. When you submit the invoice, GL entries are posted: Accounts Receivable is debited (the customer owes you) and Sales Revenue is credited (income earned). If taxes are configured, Tax Payable is also credited. The outstanding amount shows what the customer still owes."},"9":{"title":"Record Customer Payments","description":"Create a Payment Entry to record money received from the customer. Set the payment type to “Receive,” select the customer, specify the amount, and allocate it against the Sales Invoice. You can make partial payments — the invoice’s outstanding amount updates accordingly. Create additional Payment Entries until the invoice is fully paid.","tip":"Partial payments are common. A 10,000 invoice might be paid as 3,000 now and 7,000 later. Each Payment Entry reduces the outstanding amount."},"10":{"title":"Stock Entries (Manual Inventory)","description":"Use Stock Entries for inventory movements that aren’t tied to purchases or sales. Material Receipt adds stock (opening balances, adjustments). Material Issue removes stock (write-offs, internal consumption). Material Transfer moves stock between warehouses. Each entry updates the stock ledger with moving-average valuation.","tip":"For purchased goods, use Purchase Receipts instead of Stock Entries — they link to the Purchase Order and give you a proper audit trail."},"11":{"title":"Journal Entries","description":"Journal Entries are manual accounting adjustments — expense accruals, corrections, reclassifications, opening balances. Each entry must have balanced debits and credits (total debit = total credit). Use these when no other document type fits."},"12":{"title":"Salary Payments","description":"Lambda ERP handles salaries through the existing accounting tools. First, accrue the salary expense: create a Journal Entry that debits Salary Expense and credits Salary Payable for the total payroll amount. Then pay the employees: create another Journal Entry (or Payment Entry) that debits Salary Payable and credits your bank account. This two-step process keeps your books accurate — the expense is recorded in the right period, and the cash outflow is tracked separately.","tip":"You can ask the AI assistant to do this for you: “Accrue 15,000 in salaries for April” followed by “Pay the April salaries from bank.” It will create the right journal entries automatically."},"13":{"title":"Returns and Credit Notes","description":"When a customer returns goods or you need to issue a credit, create a return. Returns use the same document type with negative quantities. Open a submitted Sales Invoice and create a Credit Note (Sales Invoice return) — this reverses the GL entries and reduces the original invoice’s outstanding amount. For stock, create a Delivery Note return to bring goods back into the warehouse. On the buying side, create a Debit Note (Purchase Invoice return) to reverse a supplier bill, or a Purchase Receipt return to send goods back.","tip":"A Credit Note is just a Sales Invoice with is_return=1 and negative quantities. The same GL logic runs — negative amounts automatically flip to the correct debit/credit sides. For a full sales return, you need both a Credit Note (financials) and a Delivery Note return (stock)."},"14":{"title":"Run Reports","description":"Check your books. The Profit & Loss shows income vs expenses and net profit for a period. The Balance Sheet shows your financial position (assets = liabilities + equity). AR Aging shows who owes you money and how overdue it is. AP Aging shows what you owe suppliers. The Trial Balance verifies double-entry integrity. The General Ledger shows every individual posting. Stock Balance shows current inventory."},"15":{"title":"Working in Foreign Currencies","description":"Your books are kept in a single base currency (chosen at company setup), but you can transact in any currency. Set a currency on an invoice or bill — or give a customer or supplier a default currency — and the exchange rate for that date is looked up automatically and stored on the document. The document keeps its amounts in its own currency, while the General Ledger always posts in your base currency. When you later collect or pay at a different rate, the realized exchange gain or loss is booked automatically to an Exchange Gain/Loss account. You can even hold a foreign-currency bank balance and convert it later at your bank’s rate — the difference versus its carried value is realized then. At month end you can revalue open foreign balances to the closing rate (an unrealized gain/loss that reverses next period), and you can view any financial statement translated into another currency for display.","tip":"This is easiest through the AI chat — try “Create a sales invoice for Lumiere Audio in EUR,” “Show me the balance sheet in EUR,” or “What’s our unrealized FX exposure at month end?” The seeded demo already includes a EUR customer (Lumiere Audio SARL) whose invoice was collected at a different rate, plus an open EUR supplier bill — open the General Ledger to see the realized FX postings."}},"caTitle":"Dynamic Analytics — Chat Your Way to Any Chart","caBadge":"Signature Feature","caBody":"Need a report the presets can’t give you? Just describe it in chat. The assistant calls a report-specialist model that creates a validated declarative report over your ERP data and returns a sharable link. The chart or table renders in seconds and is saved as a draft under Custom Analytics in the sidebar — you can reopen it, share the URL with teammates, or ask the assistant to refine it.","caTryAsking":"Try asking (click to prefill in chat):","caPrompt1":"Show me top 10 customers by revenue this year as a bar chart","caPrompt2":"Monthly purchases trend by supplier over all months and label x-axis with supplier name","caPrompt3":"Best selling items by quantity, with a table and a chart","caPrompt4":"Which customers owe me the most right now?","caTryInChat":"Try it in chat","caOpenWorkspace":"Open Analytics workspace","flowTitle":"Document Flow Overview","salesCycle":"Sales Cycle","flowShortcut":"Shortcut: Quotation can skip Sales Order and go directly to Sales Invoice or Delivery Note","purchaseCycle":"Purchase Cycle","returns":"Returns","legendStock":"stock impact","legendGl":"GL impact","legendCash":"cash impact","legendReversal":"reversal","lifecycleTitle":"Document Lifecycle","lcDraftLabel":"Draft:","lcDraftDesc":"Editable. No financial or stock impact.","lcSubmittedLabel":"Submitted:","lcSubmittedDesc":"Locked. GL entries and stock ledger entries are posted.","lcCancelledLabel":"Cancelled:","lcCancelledDesc":"All entries reversed. Document is permanently archived.","lcNoDelete":"There is no delete. To void a draft: submit it, then cancel it.","lcCorrect":"To correct a submitted document: cancel it and create a new one.","pageTitle":"Getting Started with Lambda ERP","intro":"Lambda ERP is an AI Native ERP System, with all functionality — like creating orders, checking inventory, recording payments — being accessible through a chat interface. It instructs, reasons and checks all your daily business tasks.","openChat":"Open AI Chat","tipLabel":"Tip:","topTip":"The AI chat can also give you direct links to documents and downloadable PDFs. Just ask it to create or look up a document and it will include clickable links in its response.","manualIntro":"If you want to fully understand how things work under the hood, or prefer to do things manually, here’s a full business cycle step by step.","readyTitle":"You're Ready","readyBody1":"You now understand the full cycle. A typical real-world scenario looks like this: you receive a customer order (Quotation → Sales Order), check stock, buy what you need (Purchase Order → Purchase Receipt → Purchase Invoice → Payment), deliver to the customer (Delivery Note), bill them (Sales Invoice), and collect payment (Payment Entry). If something comes back, create a Credit Note and Delivery Note return to reverse the financials and stock. The Trial Balance confirms everything is balanced.","readyBody2":"You can also use the AI chat to do all of this through natural conversation — just describe what you need and it will create the right documents for you.","setupTitle":"Set Up Your Company — Guided by AI","setupBadge":"Start Here","setupBody":"Tell the assistant what kind of business you run, and it builds your chart of accounts for you — tailored to your sector, with the right accounts and sensible defaults already wired up. It explains each decision and only creates anything once you approve.","setupSectors":"Tailored for services, retail & POS, hospitality, wholesale & distribution, import/export, manufacturing, and construction.","setupGetStarted":"Get started in chat","setupManual":"Prefer a manual form? Use the classic setup.","setupManualLink":"Classic setup","setupPrompt":"I'd like to set up my company. Can you guide me through it?"}`), Go = { title: "Formatting help", intro: "The Notes / Terms text supports light formatting on the PDF:", heading: "bold heading", emphasis: "italic / bold", rule: "horizontal divider line", price: "right-aligned price beside the text above (e.g. Monthly | CHF 380.—)", block: "Separate blocks with a blank line." }, Ho = { period: "Period", allTime: "All time", semantics: "Balance-sheet accounts: closing balance at period end. P&L accounts: movement within the period. Click an account for its ledger.", empty: "No accounts found. Complete the company setup first.", disabled: "disabled", rootType: { Asset: "Assets", Liability: "Liabilities", Equity: "Equity", Income: "Income", Expense: "Expenses" } }, Qo = { fleetCalendar: "Fleet Calendar", yard: "Yard", machineType: "Machine type", prev: "Prev", today: "Today", next: "Next", weeks: "weeks", newBooking: "New booking", reserved: "Reserved", onHire: "On Hire", machine: "Machine", loadError: "Could not load the fleet calendar.", noAssets: "No assets to show. Add machines under Rentals → Fleet.", pooledNote: "{{count}} pool booking(s) awaiting assignment", unassigned: "Pool — unassigned", poolWarning: "Pool capacity is reserved. No specific machine has been assigned yet; assign a machine before dispatch." }, Yo = { Unit: "Specific machine", Pool: "Pool – assign machine later", Available: "Available", "On Hire": "On Hire", Out: "Out", Maintenance: "Maintenance", Retired: "Retired", Reserved: "Reserved", Returned: "Returned", Cancelled: "Cancelled", Customer: "Customer", Supplier: "Supplier" }, Jo = { asset: { item_code: "The Item is the machine TYPE and carries the rate; an Asset is one physical unit of it. The Item must have is_asset_tracked = 1.", asset_tag: "Plate or serial — must be unique across assets.", status: "The unit's state today (not a date range). Retired units leave the pool.", meter_reading: "Operating hours / km." }, reservation: { item_code: "Pooled booking ('a 1.7t excavator'): set Item + Yard + Qty. For a specific machine, set Asset instead.", asset: "Book one specific machine. A unit booking also consumes a slot of its Item+Yard pool.", from_datetime: "Exact start — date and time (e.g. 14:27). A bare date means 00:00.", to_datetime: "Exact end. Windows are half-open [from, to): a hire ending 09:00 and the next starting 09:00 do NOT clash." } }, Zo = { label: "Default sales tax", none: "No automatic tax application", hint: "Applied to new quotations, orders and invoices when taxes are not explicitly provided. Existing documents remain unchanged. Use Save to apply.", saveFirst: "Save the company first, then select one of its sales tax templates.", loadError: "Tax templates could not be loaded. The saved selection is preserved.", retry: "Try again", unavailable: "The saved template is not available as a sales tax template for this company. Please check the selection.", empty: "This company has no sales tax templates yet." }, Xo = { title: "Connect an app", subtitle: "Choose where you want to use Lambda ERP. We’ll show only the setup you need.", other: "Something else", customName: "Custom integration", signIn: "Sign in", apiKey: "API key", access: "Access", role_viewer: "View data", role_manager: "Create and edit", role_admin: "Full administrative access", roleHelp: "Access is always limited by your own ERP permissions. Choose only what this app needs.", copied: "Copied", copyFailed: "Could not copy automatically. Select the text and copy it manually.", retry: "Try again", interface: "Connection type", chatDisabled: "Chat API access is disabled. Ask an administrator to enable it in the API settings above.", restDisabled: "REST API / MCP access is disabled. An administrator must enable it in the API settings before this app can connect.", chatgptStep: "In ChatGPT, add a custom app / connector. Developer mode and workspace permission may be required.", claudeStep: "In Claude, open Connectors and choose Add custom connector.", urlStep: "Paste this server URL. Use OAuth sign-in; leave optional client ID and client secret fields empty.", approveStep: "Connect, sign in to Lambda ERP, and approve the access you want to grant.", noKeyNeeded: "No API key or custom header is needed. Access is chosen on the approval screen.", officialGuide: "Open the app’s setup guide", copyUrl: "Copy server URL", keyReady: "Your connection key is ready", copySetup: "Copy setup", copyHeader: "Copy authorization header", cliStep: "Run this command in your terminal, then open /mcp in Claude Code to check the connection.", codexStep: "Add this entry to your Codex configuration, then restart Codex and check /mcp.", done: "Done — hide secret", existing: "Existing connections", refresh: "Refresh", lastUsed: "Last used", notUsed: "Not used yet", consentTitle: "Connect to Lambda ERP", expired: "This connection request is missing or expired. Start again in your app.", loginFirst: "Sign in to your ERP account to review this app’s request.", consentBody: "“{{app}}” is requesting access to your ERP. Choose the access you want to allow.", returnTo: "After approval, you will return to", revokeHelp: "You can revoke this connection at any time in Settings → API access.", deny: "Cancel", allow: "Allow connection", explain_viewer: "The app can read ERP records and reports and generate document PDFs. It cannot edit business records.", explain_manager: "The app can also create, edit, submit and cancel business documents and update master data.", explain_admin: "The app can use all available ERP tools, including administrative actions and deleting master data." }, el = {
8115
+ const Co = { incomplete: "Draft saved; PDF output requires Customer, Company and at least one Quotation." }, So = { save: "Save", saving: "Saving...", submit: "Submit", cancel: "Cancel", cancelDoc: "Cancel", cancelDocAbort: "Don't cancel", cancelDocConfirmBtn: "Yes, cancel document", cancelBooking: "Cancel booking", cancelBookingConfirm: "Yes, cancel booking", keepBooking: "Keep booking", discardDraft: "Discard draft", keepDraft: "Keep", discardDraftConfirm: "Discard draft", showDiscarded: "Show discarded", showDisabled: "Show disabled", columns: "Columns", searchPlaceholder: "Search…", noRecords: "No records found", active: "Active", disabled: "Disabled", sortByColumn: "Sort by this column", delete: "Delete", edit: "Edit", pdf: "PDF", addRow: "Add Row", loading: "Loading...", enable: "Enable", disable: "Disable", refresh: "Refresh", refreshing: "Refreshing", areYouSure: "Are you sure?", all: "All", apply: "Apply", new: "New", prev: "Prev", next: "Next", perPage: "Per page", page: "Page", of: "of", showing: "Showing", total: "Total", totals: "Totals", errorOccurred: "An error occurred" }, Ao = { today: "Today", thisWeek: "This Week", thisMonth: "This Month", thisQuarter: "This Quarter", thisYear: "This Year", lastMonth: "Last Month", lastQuarter: "Last Quarter", lastYear: "Last Year", endOfThisMonth: "End of This Month", endOfLastMonth: "End of Last Month", endOfThisQuarter: "End of This Quarter", endOfThisYear: "End of This Year", endOfLastYear: "End of Last Year" }, _o = { title: "Bank statement import", subtitle: "Preview and import ISO 20022 CAMT statements without posting to the ledger.", selectFiles: "CAMT files", chooseFiles: "Choose XML or ZIP files", dropFiles: "Drop XML or ZIP files here", unsupportedFiles: "Only CAMT XML or ZIP files are supported: {{files}}", fileHint: "camt.053.001.04 and camt.053.001.08 · multiple files supported", preview: "Show preview", noPosting: "Previewing and importing do not post accounting entries.", alreadyImported: "Already imported", period: "Period", openingBalance: "Opening balance", closingBalance: "Closing balance", entries: "Entries", credits: "Credits", debits: "Debits", details: "Transaction details", batches: "batches", qrReferences: "QR references", mapping: "Bank account mapping", selectBankAccount: "Select a Bank Account…", noBankAccounts: "No Bank Account has been configured yet.", createBankAccount: "Create Bank Account", mappingRequired: "Map every new statement to a Bank Account before importing.", import: "Import bank statements", importComplete: "Import completed", importCounts: "{{imported}} entries imported, {{duplicates}} duplicates skipped", history: "Recent imports", importId: "Import", bankAccount: "Bank account", source: "Source file", openReconciliation: "Open bank reconciliation" }, Do = { title: "Bank reconciliation", subtitle: "Match imported bank transactions to posted vouchers, open invoices, or ledger accounts.", status: "Status", unreconciled: "Unreconciled", reconciled: "Reconciled", transactions: "Bank transactions", empty: "No bank transactions in this view.", remittance: "Remittance information", reference: "Reference", openTransaction: "Open bank transaction", conversionRate: "Exchange rate (1 {{currency}} in {{base}})", conversionRateHint: "Required when no exchange rate exists for the transaction date.", existingVouchers: "Existing accounting entries", existingVouchersHint: "These submitted vouchers contain an exact movement on this bank account. Matching does not post them again.", match: "Match", openInvoices: "Open invoices", openInvoicesHint: "Select one or more invoices belonging to the same party. Confirmation creates and submits a Payment Entry.", outstanding: "Outstanding", allocation: "Allocation", noInvoiceSuggestions: "No plausible open invoice was found.", allocated: "Allocated {{amount}} of {{total}}", createPayment: "Create payment and reconcile", directPosting: "Post to a ledger account", directPostingHint: "For fees, interest and other non-invoice movements. Receivable, payable, bank and cash control accounts are intentionally excluded.", counterpartyAccount: "Counter account", remarks: "Remarks", createJournal: "Create journal entry and reconcile", activeMatch: "Active reconciliation", undo: "Undo reconciliation", confirmExistingTitle: "Match existing entry?", confirmExistingBody: "Match {{transaction}} to {{voucher}}? No new ledger entry will be posted.", confirmGroupBody: "Match all {{count}} transactions totalling {{amount}} to {{voucher}}? No new ledger entry will be posted.", groupedTransactions: "{{count}} grouped transactions", confirmPaymentTitle: "Post payment and reconcile?", confirmPaymentBody: "This submits a payment of {{amount}} for {{transaction}} and allocates it across {{count}} invoice(s).", onAccountWarning: "{{amount}} will remain as an on-account payment for this party.", confirmJournalTitle: "Post journal entry and reconcile?", confirmJournalBody: "This posts {{amount}} for {{transaction}} against {{account}}.", postAndReconcile: "Post and reconcile", undoTitle: "Undo reconciliation?", undoCreatedBody: "The voucher created by this reconciliation will be cancelled, its ledger entries reversed, and invoice allocations restored.", undoExistingBody: "The existing voucher will only be unlinked. It remains submitted and its ledger entries are not changed.", undoExistingGroupBody: "All {{count}} grouped transactions ({{transactions}}) will be unlinked together. The existing voucher remains submitted and its ledger entries are not changed.", reconciledNotice: "Bank transaction reconciled.", groupReconciledNotice: "{{count}} bank transactions reconciled as one group.", undoneNotice: "Reconciliation reversed." }, Po = { placeholder: "Search or enter field:value…", search: "Search", addFilter: "+ Filter", availableFields: "Available fields", fieldHelp: "Choose a field or type its technical name followed by a colon.", field: "Field", valueFor: "Value for {{label}}", allFields: "All fields", typeForValues: "Start typing to find values in this field.", noValues: "No matching values found.", noFields: "No matching fields found.", useValue: "Use “{{value}}”", removeFilter: "Remove {{label}} filter", types: { text: "Text", textarea: "Long text", select: "Selection", link: "Linked record", date: "Date", datetime: "Date and time", number: "Number", currency: "Amount" } }, To = { title: "Language", help: "Choose the language for the interface. Your choice is saved in this browser.", label: "Language" }, Ro = { back: "Back", logout: "Logout", openMenu: "Open menu", closeMenu: "Close menu", new: "New {{label}}" }, Bo = { dashboard: "Dashboard", setup: "Company Setup", tutorial: "Getting Started", chat: "Chat", reports: "Reports", "trial-balance": "Trial Balance", "general-ledger": "General Ledger", "stock-balance": "Stock Balance", "profit-and-loss": "Profit & Loss", "balance-sheet": "Balance Sheet", "ar-aging": "Accounts Receivable Aging", "ap-aging": "Accounts Payable Aging", analytics: "Analytics", "chart-of-accounts": "Chart of Accounts" }, Io = { quotation: { one: "Quotation", other: "Quotations" }, proposal: { one: "Proposal", other: "Proposals" }, "sales-order": { one: "Sales Order", other: "Sales Orders" }, "sales-invoice": { one: "Sales Invoice", other: "Sales Invoices" }, "pos-invoice": { one: "POS Invoice", other: "POS Invoices" }, "purchase-order": { one: "Purchase Order", other: "Purchase Orders" }, "purchase-invoice": { one: "Purchase Invoice", other: "Purchase Invoices" }, "payment-entry": { one: "Payment Entry", other: "Payment Entries" }, "journal-entry": { one: "Journal Entry", other: "Journal Entries" }, "bank-transaction": { one: "Bank Transaction", other: "Bank Transactions" }, "bank-account": { one: "Bank Account", other: "Bank Accounts" }, budget: { one: "Budget", other: "Budgets" }, subscription: { one: "Subscription", other: "Subscriptions" }, "stock-entry": { one: "Stock Entry", other: "Stock Entries" }, "delivery-note": { one: "Delivery Note", other: "Delivery Notes" }, "purchase-receipt": { one: "Purchase Receipt", other: "Purchase Receipts" }, "pricing-rule": { one: "Pricing Rule", other: "Pricing Rules" } }, Eo = { company: { one: "Company", other: "Companies" }, customer: { one: "Customer", other: "Customers" }, supplier: { one: "Supplier", other: "Suppliers" }, item: { one: "Item", other: "Items" }, warehouse: { one: "Warehouse", other: "Warehouses" } }, Lo = { chats: "Chats", newChat: "New Chat", deleteChat: "Delete chat", customAnalytics: "Custom Analytics", newAnalytics: "New Analytics", deleteReport: "Delete report", groups: { Introduction: "Introduction", Selling: "Selling", Buying: "Buying", Accounting: "Accounting", Stock: "Stock", Reports: "Reports", Masters: "Masters", Settings: "Settings", Rentals: "Rentals" }, items: { "Getting Started": "Getting Started", "Company Setup": "Company Setup", "Opening Balances": "Opening Balances", Quotation: "Quotation", Proposal: "Proposals", "Sales Order": "Sales Order", "Sales Invoice": "Sales Invoice", "POS Invoice": "POS Invoice", "Purchase Order": "Purchase Order", "Purchase Invoice": "Purchase Invoice", "Payment Entry": "Payment Entry", "Journal Entry": "Journal Entry", "Bank Transaction": "Bank Transaction", "Bank Account": "Bank Accounts", "Bank Statement Import": "Bank Statement Import", "Bank Reconciliation": "Bank Reconciliation", Budget: "Budget", Subscription: "Subscription", "Stock Entry": "Stock Entry", "Delivery Note": "Delivery Note", "Purchase Receipt": "Purchase Receipt", "Trial Balance": "Trial Balance", "Profit & Loss": "Profit & Loss", "Balance Sheet": "Balance Sheet", "General Ledger": "General Ledger", "AR Aging": "AR Aging", "AP Aging": "AP Aging", Analytics: "Analytics", "Stock Balance": "Stock Balance", Company: "Company", Customer: "Customer", Supplier: "Supplier", Item: "Item", Warehouse: "Warehouse", General: "General", "Pricing Rule": "Pricing Rule", "Users & Team": "Users & Team", "Chart of Accounts": "Chart of Accounts", "Fleet Calendar": "Fleet Calendar", "Fleet (Assets)": "Fleet (Assets)", Reservations: "Reservations" } }, Oo = { taglineFirstRun: "Create your admin account to get started", taglineRegister: "Create your account", taglineSignIn: "Sign in to your account", demoBanner: "Demo mode is active. Sign in with an admin account to manage settings or disable public access.", fullName: "Full Name", email: "Email", password: "Password", confirmPassword: "Confirm Password", createAdmin: "Create Admin Account", register: "Register", signIn: "Sign In", browsingTitle: "Just browsing?", browsingBody: "Start a fresh chat session and watch the live demo.", enterDemo: "Enter Live Demo", haveInvite: "Have an invite?", noAccountPrompt: "Need an account?", registerLink: "Register", alreadyHaveAccount: "Already have an account?", signInLink: "Sign in", passwordsNoMatch: "Passwords do not match", passwordTooShort: "Password must be at least 6 characters", registrationFailed: "Registration failed", loginFailed: "Login failed", orContinueWith: "or continue with", continueWithProvider: "Continue with {{provider}}", oauthError: { generic: "Sign-in failed. Please try again.", email_exists: "An account with this email already exists. Sign in, then link this provider from your account settings.", email_unverified: "Your provider did not confirm a verified email, so we can't sign you in.", registration_closed: "This email has no account yet and registration is invite-only.", invite_invalid: "That invite is invalid, already used, or issued for a different email.", identity_taken: "That provider account is already linked to a different user.", account_disabled: "This account has been disabled.", token_exchange_failed: "Sign-in failed while contacting the provider. Please try again." } }, Fo = { pdfTitle: "PDF & Print", pageSize: "Page Size", pdfHelp: "A4 is standard internationally (210 x 297 mm). Letter is standard in the US (8.5 x 11 in).", passwordTitle: "Change Password", currentPassword: "Current password", newPassword: "New password", confirmNewPassword: "Confirm new password", changePassword: "Change password", changingPassword: "Changing...", passwordChanged: "Password changed.", setPasswordTitle: "Set a Password", setPasswordBody: "Your account signs in with Google. Set a password to also sign in with email and password — a handy fallback if you ever lose access to your Google account.", setPassword: "Set password", passwordSet: "Password set.", linkedAccountsTitle: "Linked Accounts", linkedAccountsBody: "Link a Google or Apple account to sign in without a password.", linked: "Linked", linkProvider: "Link {{provider}}", openingTitle: "Opening Balances", openingBody: "The Opening Balances page allows importing account balances, stock, and outstanding invoices from a previous system.", openingEnabled: "Currently enabled — accessible under Introduction > Opening Balances.", openingDisabled: "Currently disabled — the page is hidden from the sidebar.", signupTitle: "Public Signup", signupBody: "Allow anyone to create their own account (as a viewer) without an invite. When off, new users can only join via an invite link.", signupEnabled: "Currently enabled — anyone with the URL can register as a viewer.", signupDisabled: "Currently disabled — registration is invite-only.", chatApiTitle: "Chat API", apiTitle: "Programmatic API", restApiBody: "Allow apps and scripts to access this ERP through REST or MCP, using API keys or OAuth account linking. Disabled by default.", restApiEnabled: "Currently enabled — issued keys can call /api/documents, /api/masters, and the rest of the REST API.", restApiDisabled: "Currently disabled — the REST API only accepts the session cookie; keys are rejected.", restApiDeactivateTitle: "Deactivate the REST API?", restApiDeactivateBody: "Bearer-key access to the REST API will be switched off — connectors and scripts are rejected until you turn it back on. The web app (session cookie) is unaffected, and your API keys are NOT deleted: they start working again the instant you re-enable the API.", chatApiBody: "Let external applications talk to this ERP's chat agent over an HTTP API, using Bearer API keys. Off by default.", chatApiEnabled: "Currently enabled — issued keys can call POST /api/v1/chat.", chatApiDisabled: "Currently disabled — the API returns 404 and keys don't work.", chatApiDeactivateTitle: "Deactivate the chat API?", chatApiDeactivateBody: "The chat API will be switched off — every programmatic request (Bearer API keys) is rejected until you turn it back on. Your API keys are NOT deleted: they stay saved and start working again the instant you re-enable the API.", chatApiDeactivateConfirm: "Deactivate API", chatApiKeysTitle: "Apps and API keys", chatApiTokenOnce: "Copy this token now — it won't be shown again.", chatApiCopy: "Copy", chatApiDismiss: "Dismiss", chatApiRevoked: "revoked", chatApiRevoke: "Revoke", chatApiNoKeys: "No connections yet.", chatApiName: "Name", chatApiRole: "Role", chatApiCreate: "Create key", chatApiWarn: "A key acts with your identity and at most your role — treat it like a password. Revoke first, then delete.", publicTitle: "Public Access (Demo Mode)", publicActive: "Active", publicActiveBody: "Public access is enabled. Anyone can use the application without logging in. All visitors get manager-level permissions.", disablePublic: "Disable Public Access", disabling: "Disabling...", publicInactiveBody: "Enable public access to let anyone use the application without an account. Useful for demos and showcases. All visitors get manager-level permissions and share the same identity.", enablePublic: "Enable Public Access", enableWarning: "This will allow anyone to access the application without logging in. They will be able to create, edit, and submit documents. Your admin account still requires login.", yesEnable: "Yes, Enable", enabling: "Enabling...", tokenSpendTitle: "Token Spend", chatApiDelete: "Delete", chatApiYourKeys: "Your connections", chatApiYouBadge: "you", chatApiUnknownUser: "Unknown user", chatApiAdminAllNote: "As an administrator, you can manage everyone’s connections. Your own connections appear first.", chatApiConfirmTitle: "This connection belongs to another user", chatApiConfirmRevokeBody: "“{{key}}” belongs to {{name}}. Revoking it will immediately disconnect their app. Continue?", chatApiConfirmDeleteBody: "“{{key}}” belongs to {{name}}. Permanently remove this revoked connection?", chatApiPersonal: "Connect apps using your ERP account. Access never exceeds your current permissions.", version: "Version {{version}}" }, zo = { Draft: "Draft", Open: "Open", Submitted: "Submitted", "To Deliver and Bill": "To Deliver and Bill", "To Deliver": "To Deliver", "To Bill": "To Bill", Completed: "Completed", Ordered: "Ordered", Paid: "Paid", Unpaid: "Unpaid", Cancelled: "Cancelled", Discarded: "Discarded", Overdue: "Overdue", Return: "Return", Closed: "Closed" }, Mo = { periodRange: "Reporting period: {{from}} – {{to}}", periodFrom: "Reporting period from {{from}}", periodThrough: "Reporting period through {{to}}", asOfDate: "As of: {{date}}", balanced: "BALANCED", imbalanced: "IMBALANCED", income: "Income", expenses: "Expenses", totalIncome: "Total Income", totalExpenses: "Total Expenses", netProfit: "Net Profit", netLoss: "Net Loss", assets: "Assets", liabilities: "Liabilities", equity: "Equity", totalAssets: "Total Assets", totalLiabilities: "Total Liabilities", totalEquity: "Total Equity", totalLiabilitiesEquity: "Total Liabilities + Equity", current: "Current", noData: "No data found", noEntries: "No entries found", noStockData: "No stock data found", noReceivables: "No outstanding receivables", noPayables: "No outstanding payables", noDocuments: "No documents found" }, qo = { required: "Required: {{fields}}", deleteConfirm: "Delete this {{label}}?", disabledNotice: "This {{label}} is disabled. It remains in the system because other records still reference it.", disabledOnDelete: "{{label}} {{name}} was disabled instead of deleted because it is referenced by {{reason}}." }, $o = { totalRevenue: "Total Revenue", outstandingReceivable: "Outstanding Receivable", outstandingPayable: "Outstanding Payable", totalStockValue: "Total Stock Value", recentDocuments: "Recent Documents", noRecentDocuments: "No recent documents" }, Vo = { title: "Lambda ERP Chat", subtitle: "Ask me to create documents, look up data, or run reports.", s1: "What customers do we have?", s2: "Show me the trial balance", s3: "Create a quotation for 10 Bolt Pack M8", s4: "List all unpaid invoices", loadOlder: "Load older messages", uploading: "Uploading...", failed: "Failed", remove: "Remove", placeholderDemo: "Running live demo...", placeholderType: "Type a message...", placeholderConnecting: "Connecting...", attachTitle: "Attach file (PDF or image, max 10 MB)", creating: "Creating chat...", dropToAttach: "Drop file to attach (images or PDF, max 10 MB each)", demoError: "Could not start demo", voiceStart: "Record voice message", voiceStop: "Stop recording", voiceTranscribing: "Transcribing...", micDenied: "Microphone access was denied.", transcribeFailed: "Transcription failed. Please try again." }, Uo = { "Create Credit Note": "Create Credit Note", "Create Debit Note": "Create Debit Note", "Create Delivery Note": "Create Delivery Note", "Create Purchase Invoice": "Create Purchase Invoice", "Create Purchase Receipt": "Create Purchase Receipt", "Create Return": "Create Return", "Create Sales Invoice": "Create Sales Invoice", "Create Sales Order": "Create Sales Order" }, Ko = { "Last modified": "Last modified", Created: "Created", "% Billed": "% Billed", "% Delivered": "% Delivered", Account: "Account", Address: "Address", Allocated: "Allocated", Amount: "Amount", "As of Date": "As of Date", Balance: "Balance", "Bank Account": "Bank Account", "Billing Interval": "Billing Interval", "Budget Amount": "Budget Amount", Buying: "Buying", Change: "Change", City: "City", Company: "Company", "Company Name": "Company Name", "Cost Center": "Cost Center", Country: "Country", Credit: "Credit", "Credit Limit": "Credit Limit", Currency: "Currency", "Current Period End": "Current Period End", Website: "Website", "Contact Person": "Contact Person", "Contact Email": "Contact Email", "Contact Phone": "Contact Phone", "Current Period Start": "Current Period Start", Customer: "Customer", "Customer Group": "Customer Group", "Customer Name": "Customer Name", Date: "Date", Debit: "Debit", "Delivery Date": "Delivery Date", Deposit: "Deposit", Description: "Description", "Discount %": "Discount %", "Discount Amt": "Discount Amt", "Discount Percentage": "Discount Percentage", "Due Date": "Due Date", Email: "Email", Enabled: "Enabled", "End Date": "End Date", "Expense Account": "Expense Account", "Fiscal Year": "Fiscal Year", "From Date": "From Date", "Grand Total": "Grand Total", Frequency: "Frequency", ID: "ID", "If Exceeded": "If Exceeded", "Income Account": "Income Account", "Incoming Value": "Incoming Value", Invoice: "Invoice", Item: "Item", "Item Code": "Item Code", "Item Group": "Item Group", "Item Name": "Item Name", "Matched Doc": "Matched Doc", "Matched Type": "Matched Type", "Min Qty": "Min Qty", Mode: "Mode", Month: "Month", Name: "Name", "Net Total": "Net Total", "Notes / Terms": "Notes / Terms", "Outgoing Value": "Outgoing Value", Outstanding: "Outstanding", "Outstanding Amount": "Outstanding Amount", "Paid Amount": "Paid Amount", "Paid From": "Paid From", "Paid To": "Paid To", Party: "Party", "Party Type": "Party Type", "Payment Type": "Payment Type", Phone: "Phone", "Plan Items": "Plan Items", "Posting Date": "Posting Date", Priority: "Priority", Qty: "Qty", Rate: "Rate", "Rate (%)": "Rate (%)", "Rate Or Discount": "Rate Or Discount", "Reference No": "Reference No", Remark: "Remark", Selling: "Selling", Source: "Source", "Source Warehouse": "Source Warehouse", "Standard Rate": "Standard Rate", "Start Date": "Start Date", Status: "Status", "Stock Entry Type": "Stock Entry Type", "Stock UOM": "Stock UOM", "Stock Value": "Stock Value", Supplier: "Supplier", "Supplier Group": "Supplier Group", "Supplier Name": "Supplier Name", Target: "Target", "Target Warehouse": "Target Warehouse", Tax: "Tax", "Tax ID": "Tax ID", Territory: "Territory", Title: "Title", "To Date": "To Date", Total: "Total", "Total Amount": "Total Amount", "Total Credit": "Total Credit", "Total Debit": "Total Debit", "Transaction Date": "Transaction Date", Type: "Type", Unallocated: "Unallocated", "Update Stock": "Update Stock", "Valid From": "Valid From", "Valid Till": "Valid Till", "Valid Upto": "Valid Upto", "Valuation Rate": "Valuation Rate", "Voucher No": "Voucher No", "Voucher Type": "Voucher Type", Warehouse: "Warehouse", "Warehouse Name": "Warehouse Name", Withdrawal: "Withdrawal", "Zip Code": "Zip Code", "Item (type)": "Item (type)", "Asset Tag / Plate": "Asset Tag / Plate", "Home Yard": "Home Yard", "Purchase Value": "Purchase Value", "Meter Reading": "Meter Reading", "Meter Unit": "Meter Unit", Yard: "Yard", "Asset (specific unit)": "Asset (specific unit)", Allocation: "Allocation", From: "From", To: "To", Purpose: "Purpose", "Purchase Date": "Purchase Date", Notes: "Notes" }, jo = { accounts: "accounts", items: "items", monthly_distribution: "monthly_distribution", payments: "payments", plans: "plans", references: "references", taxes: "taxes" }, Wo = /* @__PURE__ */ JSON.parse(`{"chips":{"Cancelled":"Cancelled","Credit Note":"Credit Note","DN Return":"DN Return","Debit Note":"Debit Note","Delivery Note":"Delivery Note","Draft":"Draft","Payment Entry":"Payment Entry","Purchase Invoice":"Purchase Invoice","Purchase Order":"Purchase Order","Purchase Receipt":"Purchase Receipt","Quotation":"Quotation","Sales Invoice":"Sales Invoice","Sales Order":"Sales Order","Submitted":"Submitted"},"links":{"AP Aging":"AP Aging","AR Aging":"AR Aging","Balance Sheet":"Balance Sheet","Customers":"Customers","Delivery Notes":"Delivery Notes","General Ledger":"General Ledger","Go to Setup":"Go to Setup","Items":"Items","New Journal Entry":"New Journal Entry","New Payment Entry":"New Payment Entry","New Purchase Order":"New Purchase Order","New Quotation":"New Quotation","New Stock Entry":"New Stock Entry","Open AI Chat":"Open AI Chat","Profit & Loss":"Profit & Loss","Purchase Invoices":"Purchase Invoices","Purchase Receipts":"Purchase Receipts","Sales Invoices":"Sales Invoices","Stock Balance":"Stock Balance","Suppliers":"Suppliers","Trial Balance":"Trial Balance","View Quotations":"View Quotations","Warehouses":"Warehouses"},"steps":{"1":{"title":"Set Up Your Company","description":"Create a company with a base currency. This generates your Chart of Accounts (30 standard accounts across Assets, Liabilities, Equity, Income, and Expenses) and a default Cost Center. You can also seed demo data with sample customers, suppliers, and items to get started quickly."},"2":{"title":"Create Master Data","description":"Before you can transact, you need master records. Create at least one Customer, one Supplier, one Item (with a standard rate), and one Warehouse. If you seeded demo data in Step 1, these already exist.","tip":"Items have a standard rate that auto-fills when you add them to documents. You can always override the rate per transaction."},"3":{"title":"Create a Quotation","description":"A Quotation is a non-binding offer to a customer. Select a customer, add line items with quantities and rates, optionally add tax rows, then save. Quotations have no financial impact — they don't create accounting or stock entries. Set a validity date so the offer expires automatically."},"4":{"title":"Submit and Convert to Sales Order","description":"Open your saved Quotation and click Submit to confirm it. Then click “Create Sales Order” to convert it. The Sales Order represents a confirmed commitment from the customer. It still has no financial impact, but it reserves stock for planning purposes.","tip":"Only submitted documents can be converted to the next step. Draft → Submit → Convert is the standard flow. For quick deals, you can skip the Sales Order and go directly from Quotation to Sales Invoice or Delivery Note."},"5":{"title":"Check Stock Before Fulfilling","description":"Before you can deliver, check if you actually have the item in stock. Go to the Stock Balance report and look up the item. If your warehouse has zero quantity, you’ll need to bring stock in first — either through a Purchase Order (buying from a supplier) or a Stock Entry (manual receipt).","tip":"This is a common real-world scenario: you sell something, then realize you need to buy it first. The ERP handles both flows."},"6":{"title":"Purchase Cycle: Buy Stock from a Supplier","description":"If you need to buy stock, you have two valid paths. The standard path is Purchase Order -> Purchase Receipt -> Purchase Invoice: use this when goods arrive before or separately from the supplier bill. If the bill and receipt happen together, you can create the Purchase Invoice directly and enable Update Stock so the same document both receives inventory and records Accounts Payable.","tip":"Use Purchase Receipt first when receiving goods separately. Use Purchase Invoice with Update Stock when one step should both receive stock and book the supplier bill. In that direct path, set a warehouse on each stock item row."},"7":{"title":"Create a Delivery Note","description":"Now that you have stock, go back to your submitted Sales Order and click “Create Delivery Note.” Set the warehouse on each item row (where the goods ship from), then submit. This moves inventory out of the warehouse — your stock balance decreases.","tip":"The Delivery Note is the shipping document. It reduces stock but doesn’t create an invoice. You can deliver and invoice separately."},"8":{"title":"Create and Submit the Sales Invoice","description":"From the submitted Sales Order, create a Sales Invoice. When you submit the invoice, GL entries are posted: Accounts Receivable is debited (the customer owes you) and Sales Revenue is credited (income earned). If taxes are configured, Tax Payable is also credited. The outstanding amount shows what the customer still owes."},"9":{"title":"Record Customer Payments","description":"Create a Payment Entry to record money received from the customer. Set the payment type to “Receive,” select the customer, specify the amount, and allocate it against the Sales Invoice. You can make partial payments — the invoice’s outstanding amount updates accordingly. Create additional Payment Entries until the invoice is fully paid.","tip":"Partial payments are common. A 10,000 invoice might be paid as 3,000 now and 7,000 later. Each Payment Entry reduces the outstanding amount."},"10":{"title":"Stock Entries (Manual Inventory)","description":"Use Stock Entries for inventory movements that aren’t tied to purchases or sales. Material Receipt adds stock (opening balances, adjustments). Material Issue removes stock (write-offs, internal consumption). Material Transfer moves stock between warehouses. Each entry updates the stock ledger with moving-average valuation.","tip":"For purchased goods, use Purchase Receipts instead of Stock Entries — they link to the Purchase Order and give you a proper audit trail."},"11":{"title":"Journal Entries","description":"Journal Entries are manual accounting adjustments — expense accruals, corrections, reclassifications, opening balances. Each entry must have balanced debits and credits (total debit = total credit). Use these when no other document type fits."},"12":{"title":"Salary Payments","description":"Lambda ERP handles salaries through the existing accounting tools. First, accrue the salary expense: create a Journal Entry that debits Salary Expense and credits Salary Payable for the total payroll amount. Then pay the employees: create another Journal Entry (or Payment Entry) that debits Salary Payable and credits your bank account. This two-step process keeps your books accurate — the expense is recorded in the right period, and the cash outflow is tracked separately.","tip":"You can ask the AI assistant to do this for you: “Accrue 15,000 in salaries for April” followed by “Pay the April salaries from bank.” It will create the right journal entries automatically."},"13":{"title":"Returns and Credit Notes","description":"When a customer returns goods or you need to issue a credit, create a return. Returns use the same document type with negative quantities. Open a submitted Sales Invoice and create a Credit Note (Sales Invoice return) — this reverses the GL entries and reduces the original invoice’s outstanding amount. For stock, create a Delivery Note return to bring goods back into the warehouse. On the buying side, create a Debit Note (Purchase Invoice return) to reverse a supplier bill, or a Purchase Receipt return to send goods back.","tip":"A Credit Note is just a Sales Invoice with is_return=1 and negative quantities. The same GL logic runs — negative amounts automatically flip to the correct debit/credit sides. For a full sales return, you need both a Credit Note (financials) and a Delivery Note return (stock)."},"14":{"title":"Run Reports","description":"Check your books. The Profit & Loss shows income vs expenses and net profit for a period. The Balance Sheet shows your financial position (assets = liabilities + equity). AR Aging shows who owes you money and how overdue it is. AP Aging shows what you owe suppliers. The Trial Balance verifies double-entry integrity. The General Ledger shows every individual posting. Stock Balance shows current inventory."},"15":{"title":"Working in Foreign Currencies","description":"Your books are kept in a single base currency (chosen at company setup), but you can transact in any currency. Set a currency on an invoice or bill — or give a customer or supplier a default currency — and the exchange rate for that date is looked up automatically and stored on the document. The document keeps its amounts in its own currency, while the General Ledger always posts in your base currency. When you later collect or pay at a different rate, the realized exchange gain or loss is booked automatically to an Exchange Gain/Loss account. You can even hold a foreign-currency bank balance and convert it later at your bank’s rate — the difference versus its carried value is realized then. At month end you can revalue open foreign balances to the closing rate (an unrealized gain/loss that reverses next period), and you can view any financial statement translated into another currency for display.","tip":"This is easiest through the AI chat — try “Create a sales invoice for Lumiere Audio in EUR,” “Show me the balance sheet in EUR,” or “What’s our unrealized FX exposure at month end?” The seeded demo already includes a EUR customer (Lumiere Audio SARL) whose invoice was collected at a different rate, plus an open EUR supplier bill — open the General Ledger to see the realized FX postings."}},"caTitle":"Dynamic Analytics — Chat Your Way to Any Chart","caBadge":"Signature Feature","caBody":"Need a report the presets can’t give you? Just describe it in chat. The assistant calls a report-specialist model that creates a validated declarative report over your ERP data and returns a sharable link. The chart or table renders in seconds and is saved as a draft under Custom Analytics in the sidebar — you can reopen it, share the URL with teammates, or ask the assistant to refine it.","caTryAsking":"Try asking (click to prefill in chat):","caPrompt1":"Show me top 10 customers by revenue this year as a bar chart","caPrompt2":"Monthly purchases trend by supplier over all months and label x-axis with supplier name","caPrompt3":"Best selling items by quantity, with a table and a chart","caPrompt4":"Which customers owe me the most right now?","caTryInChat":"Try it in chat","caOpenWorkspace":"Open Analytics workspace","flowTitle":"Document Flow Overview","salesCycle":"Sales Cycle","flowShortcut":"Shortcut: Quotation can skip Sales Order and go directly to Sales Invoice or Delivery Note","purchaseCycle":"Purchase Cycle","returns":"Returns","legendStock":"stock impact","legendGl":"GL impact","legendCash":"cash impact","legendReversal":"reversal","lifecycleTitle":"Document Lifecycle","lcDraftLabel":"Draft:","lcDraftDesc":"Editable. No financial or stock impact.","lcSubmittedLabel":"Submitted:","lcSubmittedDesc":"Locked. GL entries and stock ledger entries are posted.","lcCancelledLabel":"Cancelled:","lcCancelledDesc":"All entries reversed. Document is permanently archived.","lcNoDelete":"There is no delete. To void a draft: submit it, then cancel it.","lcCorrect":"To correct a submitted document: cancel it and create a new one.","pageTitle":"Getting Started with Lambda ERP","intro":"Lambda ERP is an AI Native ERP System, with all functionality — like creating orders, checking inventory, recording payments — being accessible through a chat interface. It instructs, reasons and checks all your daily business tasks.","openChat":"Open AI Chat","tipLabel":"Tip:","topTip":"The AI chat can also give you direct links to documents and downloadable PDFs. Just ask it to create or look up a document and it will include clickable links in its response.","manualIntro":"If you want to fully understand how things work under the hood, or prefer to do things manually, here’s a full business cycle step by step.","readyTitle":"You're Ready","readyBody1":"You now understand the full cycle. A typical real-world scenario looks like this: you receive a customer order (Quotation → Sales Order), check stock, buy what you need (Purchase Order → Purchase Receipt → Purchase Invoice → Payment), deliver to the customer (Delivery Note), bill them (Sales Invoice), and collect payment (Payment Entry). If something comes back, create a Credit Note and Delivery Note return to reverse the financials and stock. The Trial Balance confirms everything is balanced.","readyBody2":"You can also use the AI chat to do all of this through natural conversation — just describe what you need and it will create the right documents for you.","setupTitle":"Set Up Your Company — Guided by AI","setupBadge":"Start Here","setupBody":"Tell the assistant what kind of business you run, and it builds your chart of accounts for you — tailored to your sector, with the right accounts and sensible defaults already wired up. It explains each decision and only creates anything once you approve.","setupSectors":"Tailored for services, retail & POS, hospitality, wholesale & distribution, import/export, manufacturing, and construction.","setupGetStarted":"Get started in chat","setupManual":"Prefer a manual form? Use the classic setup.","setupManualLink":"Classic setup","setupPrompt":"I'd like to set up my company. Can you guide me through it?"}`), Go = { title: "Formatting help", intro: "The Notes / Terms text supports light formatting on the PDF:", heading: "bold heading", emphasis: "italic / bold", rule: "horizontal divider line", price: "right-aligned price beside the text above (e.g. Monthly | CHF 380.—)", block: "Separate blocks with a blank line." }, Ho = { period: "Period", allTime: "All time", semantics: "Balance-sheet accounts: closing balance at period end. P&L accounts: movement within the period. Click an account for its ledger.", empty: "No accounts found. Complete the company setup first.", disabled: "disabled", rootType: { Asset: "Assets", Liability: "Liabilities", Equity: "Equity", Income: "Income", Expense: "Expenses" } }, Qo = { fleetCalendar: "Fleet Calendar", yard: "Yard", machineType: "Machine type", prev: "Prev", today: "Today", next: "Next", weeks: "weeks", newBooking: "New booking", reserved: "Reserved", onHire: "On Hire", machine: "Machine", loadError: "Could not load the fleet calendar.", noAssets: "No assets to show. Add machines under Rentals → Fleet.", pooledNote: "{{count}} pool booking(s) awaiting assignment", unassigned: "Pool — unassigned", poolWarning: "Pool capacity is reserved. No specific machine has been assigned yet; assign a machine before dispatch." }, Yo = { Unit: "Specific machine", Pool: "Pool – assign machine later", Available: "Available", "On Hire": "On Hire", Out: "Out", Maintenance: "Maintenance", Retired: "Retired", Reserved: "Reserved", Returned: "Returned", Cancelled: "Cancelled", Customer: "Customer", Supplier: "Supplier" }, Jo = { asset: { item_code: "The Item is the machine TYPE and carries the rate; an Asset is one physical unit of it. The Item must have is_asset_tracked = 1.", asset_tag: "Plate or serial — must be unique across assets.", status: "The unit's state today (not a date range). Retired units leave the pool.", meter_reading: "Operating hours / km." }, reservation: { item_code: "Pooled booking ('a 1.7t excavator'): set Item + Yard + Qty. For a specific machine, set Asset instead.", asset: "Book one specific machine. A unit booking also consumes a slot of its Item+Yard pool.", from_datetime: "Exact start — date and time (e.g. 14:27). A bare date means 00:00.", to_datetime: "Exact end. Windows are half-open [from, to): a hire ending 09:00 and the next starting 09:00 do NOT clash." } }, Zo = { label: "Default sales tax", none: "No automatic tax application", hint: "Applied to new quotations, orders and invoices when taxes are not explicitly provided. Existing documents remain unchanged. Use Save to apply.", saveFirst: "Save the company first, then select one of its sales tax templates.", loadError: "Tax templates could not be loaded. The saved selection is preserved.", retry: "Try again", unavailable: "The saved template is not available as a sales tax template for this company. Please check the selection.", empty: "This company has no sales tax templates yet." }, Xo = { title: "Connect an app", subtitle: "Choose where you want to use Lambda ERP. We’ll show only the setup you need.", other: "Something else", customName: "Custom integration", signIn: "Sign in", apiKey: "API key", access: "Access", role_viewer: "View data", role_manager: "Create and edit", role_admin: "Full administrative access", roleHelp: "Access is always limited by your own ERP permissions. Choose only what this app needs.", copied: "Copied", copyFailed: "Could not copy automatically. Select the text and copy it manually.", retry: "Try again", interface: "Connection type", chatDisabled: "Chat API access is disabled. Ask an administrator to enable it in the API settings above.", restDisabled: "REST API / MCP access is disabled. An administrator must enable it in the API settings before this app can connect.", chatgptStep: "Open chatgpt.com in your browser. Select Plugins in the left sidebar, click Add (+) at the top right, then choose Create MCP App. Give it a name, such as Lambda ERP, and a short description.", chatgptDeveloperMode: "If Create MCP App is unavailable, check that Developer mode is enabled under Settings → Security and login. If it is already enabled, go straight to Plugins. Workspace permissions may also apply.", claudeStep: "In Claude Desktop, open Settings → Connectors and choose Add custom connector.", urlStep: "Paste this server URL. Use OAuth sign-in; leave optional client ID and client secret fields empty.", approveStep: "Connect, sign in to Lambda ERP, and approve the access you want to grant.", noKeyNeeded: "No API key or custom header is needed. Access is chosen on the approval screen.", officialGuide: "Open the app’s setup guide", copyUrl: "Copy server URL", keyReady: "Your connection key is ready", copySetup: "Copy setup", copyHeader: "Copy authorization header", cliStep: "Run this command in your terminal, then open /mcp in Claude Code to check the connection.", codexStep: "Add this entry to your Codex configuration, then restart Codex and check /mcp.", done: "Done — hide secret", existing: "Existing connections", refresh: "Refresh", lastUsed: "Last used", notUsed: "Not used yet", consentTitle: "Connect to Lambda ERP", expired: "This connection request is missing or expired. Start again in your app.", loginFirst: "Sign in to your ERP account to review this app’s request.", consentBody: "“{{app}}” is requesting access to your ERP. Choose the access you want to allow.", returnTo: "After approval, you will return to", revokeHelp: "You can revoke this connection at any time in Settings → API access.", deny: "Cancel", allow: "Allow connection", explain_viewer: "The app can read ERP records and reports and generate document PDFs. It cannot edit business records.", explain_manager: "The app can also create, edit, submit and cancel business documents and update master data.", explain_admin: "The app can use all available ERP tools, including administrative actions and deleting master data." }, el = {
8116
8116
  proposal: Co,
8117
8117
  common: So,
8118
8118
  datePresets: Ao,
@@ -8128,8 +8128,8 @@ const Co = { incomplete: "Draft saved; PDF output requires Customer, Company and
8128
8128
  login: Oo,
8129
8129
  settings: Fo,
8130
8130
  status: zo,
8131
- reports: qo,
8132
- masterForm: Mo,
8131
+ reports: Mo,
8132
+ masterForm: qo,
8133
8133
  dashboard: $o,
8134
8134
  chat: Vo,
8135
8135
  conversions: Uo,
@@ -8143,7 +8143,7 @@ const Co = { incomplete: "Draft saved; PDF output requires Customer, Company and
8143
8143
  hints: Jo,
8144
8144
  companyTax: Zo,
8145
8145
  connections: Xo
8146
- }, tl = { incomplete: "Entwurf gespeichert. Für das PDF werden Kunde, Unternehmen und mindestens ein Angebot benötigt." }, nl = { save: "Speichern", saving: "Speichern...", submit: "Buchen", cancel: "Abbrechen", cancelDoc: "Stornieren", cancelDocAbort: "Nicht stornieren", cancelDocConfirmBtn: "Definitiv stornieren", cancelBooking: "Buchung stornieren", cancelBookingConfirm: "Ja, stornieren", keepBooking: "Behalten", discardDraft: "Entwurf verwerfen", keepDraft: "Behalten", discardDraftConfirm: "Verwerfen", showDiscarded: "Verworfene anzeigen", showDisabled: "Deaktivierte anzeigen", columns: "Spalten", searchPlaceholder: "Suchen…", noRecords: "Keine Einträge gefunden", active: "Aktiv", disabled: "Deaktiviert", sortByColumn: "Nach dieser Spalte sortieren", delete: "Löschen", edit: "Bearbeiten", pdf: "PDF", addRow: "Zeile hinzufügen", loading: "Lädt...", enable: "Aktivieren", disable: "Deaktivieren", refresh: "Aktualisieren", refreshing: "Aktualisiert...", areYouSure: "Sind Sie sicher?", all: "Alle", apply: "Anwenden", new: "Neu", prev: "Zurück", next: "Weiter", perPage: "Pro Seite", page: "Seite", of: "von", showing: "Zeige", total: "Gesamt", totals: "Summen", errorOccurred: "Ein Fehler ist aufgetreten" }, al = { today: "Heute", thisWeek: "Diese Woche", thisMonth: "Dieser Monat", thisQuarter: "Dieses Quartal", thisYear: "Dieses Jahr", lastMonth: "Letzter Monat", lastQuarter: "Letztes Quartal", lastYear: "Letztes Jahr", endOfThisMonth: "Ende dieses Monats", endOfLastMonth: "Ende des letzten Monats", endOfThisQuarter: "Ende dieses Quartals", endOfThisYear: "Ende dieses Jahres", endOfLastYear: "Ende des letzten Jahres" }, rl = { title: "Kontoauszug importieren", subtitle: "ISO-20022-CAMT-Kontoauszüge prüfen und ohne automatische Verbuchung importieren.", selectFiles: "CAMT-Dateien", chooseFiles: "XML- oder ZIP-Dateien auswählen", dropFiles: "XML- oder ZIP-Dateien hier ablegen", unsupportedFiles: "Es werden nur CAMT-XML- oder ZIP-Dateien unterstützt: {{files}}", fileHint: "camt.053.001.04 und camt.053.001.08 · mehrere Dateien möglich", preview: "Vorschau anzeigen", noPosting: "Vorschau und Import erzeugen keine Buchungssätze.", alreadyImported: "Bereits importiert", period: "Zeitraum", openingBalance: "Anfangssaldo", closingBalance: "Endsaldo", entries: "Buchungen", credits: "Gutschriften", debits: "Belastungen", details: "Transaktionsdetails", batches: "Sammelbuchungen", qrReferences: "QR-Referenzen", mapping: "Bankkonto-Zuordnung", selectBankAccount: "Bankkonto auswählen…", noBankAccounts: "Es wurde noch kein Bankkonto eingerichtet.", createBankAccount: "Bankkonto einrichten", mappingRequired: "Ordnen Sie vor dem Import jeden neuen Auszug einem Bankkonto zu.", import: "Kontoauszüge importieren", importComplete: "Import abgeschlossen", importCounts: "{{imported}} Buchungen importiert, {{duplicates}} Duplikate übersprungen", history: "Letzte Importe", importId: "Import", bankAccount: "Bankkonto", source: "Quelldatei", openReconciliation: "Bankabstimmung öffnen" }, il = { title: "Bankabstimmung", subtitle: "Importierte Banktransaktionen mit Buchungen, offenen Rechnungen oder Sachkonten abstimmen.", status: "Status", unreconciled: "Nicht abgestimmt", reconciled: "Abgestimmt", transactions: "Banktransaktionen", empty: "In dieser Ansicht sind keine Banktransaktionen vorhanden.", remittance: "Zahlungsinformation", reference: "Referenz", openTransaction: "Banktransaktion öffnen", conversionRate: "Wechselkurs (1 {{currency}} in {{base}})", conversionRateHint: "Erforderlich, wenn für das Transaktionsdatum kein Wechselkurs hinterlegt ist.", existingVouchers: "Bestehende Buchungen", existingVouchersHint: "Diese eingereichten Belege enthalten eine exakt passende Bewegung auf diesem Bankkonto. Bei der Zuordnung wird nichts erneut gebucht.", match: "Zuordnen", openInvoices: "Offene Rechnungen", openInvoicesHint: "Wähle eine oder mehrere Rechnungen derselben Partei. Nach der Bestätigung wird eine Zahlung erstellt und eingereicht.", outstanding: "Offen", allocation: "Zuweisung", noInvoiceSuggestions: "Es wurde keine plausible offene Rechnung gefunden.", allocated: "{{amount}} von {{total}} zugewiesen", createPayment: "Zahlung erstellen und abstimmen", directPosting: "Auf ein Sachkonto buchen", directPostingHint: "Für Gebühren, Zinsen und andere Bewegungen ohne Rechnung. Debitoren-, Kreditoren-, Bank- und Kassenkonten sind bewusst ausgeschlossen.", counterpartyAccount: "Gegenkonto", remarks: "Bemerkung", createJournal: "Buchungssatz erstellen und abstimmen", activeMatch: "Aktive Abstimmung", undo: "Abstimmung rückgängig machen", confirmExistingTitle: "Bestehende Buchung zuordnen?", confirmExistingBody: "{{transaction}} mit {{voucher}} abstimmen? Es wird kein neuer Buchungssatz erzeugt.", confirmGroupBody: "Alle {{count}} Transaktionen über insgesamt {{amount}} mit {{voucher}} abstimmen? Es wird kein neuer Buchungssatz erzeugt.", groupedTransactions: "{{count}} gruppierte Transaktionen", confirmPaymentTitle: "Zahlung buchen und abstimmen?", confirmPaymentBody: "Damit wird für {{transaction}} eine Zahlung über {{amount}} eingereicht und auf {{count}} Rechnung(en) verteilt.", onAccountWarning: "{{amount}} bleibt als nicht zugewiesene Zahlung bei dieser Partei bestehen.", confirmJournalTitle: "Buchungssatz erstellen und abstimmen?", confirmJournalBody: "Damit werden {{amount}} für {{transaction}} gegen {{account}} gebucht.", postAndReconcile: "Buchen und abstimmen", undoTitle: "Abstimmung rückgängig machen?", undoCreatedBody: "Der durch diese Abstimmung erstellte Beleg wird storniert, seine Hauptbuchungen werden umgekehrt und Rechnungszuweisungen wiederhergestellt.", undoExistingBody: "Der bestehende Beleg wird nur von der Banktransaktion getrennt. Er bleibt eingereicht und seine Buchungen werden nicht verändert.", undoExistingGroupBody: "Alle {{count}} gruppierten Banktransaktionen ({{transactions}}) werden gemeinsam vom Beleg getrennt. Der bestehende Beleg bleibt eingereicht und seine Buchungen werden nicht verändert.", reconciledNotice: "Banktransaktion wurde abgestimmt.", groupReconciledNotice: "{{count}} Banktransaktionen wurden als Gruppe abgestimmt.", undoneNotice: "Abstimmung wurde rückgängig gemacht." }, sl = { placeholder: "Suchen oder Feld:Wert eingeben…", search: "Suchen", addFilter: "+ Filter", availableFields: "Verfügbare Felder", fieldHelp: "Feld auswählen oder den technischen Namen mit Doppelpunkt eingeben.", field: "Feld", valueFor: "Wert für {{label}}", allFields: "Alle Felder", typeForValues: "Tippen, um Werte in diesem Feld zu finden.", noValues: "Keine passenden Werte gefunden.", noFields: "Keine passenden Felder gefunden.", useValue: "„{{value}}“ verwenden", removeFilter: "Filter {{label}} entfernen", types: { text: "Text", textarea: "Langtext", select: "Auswahl", link: "Verknüpfter Eintrag", date: "Datum", datetime: "Datum und Uhrzeit", number: "Zahl", currency: "Betrag" } }, ol = { title: "Sprache", help: "Wählen Sie die Sprache der Oberfläche. Ihre Auswahl wird in diesem Browser gespeichert.", label: "Sprache" }, ll = { back: "Zurück", logout: "Abmelden", openMenu: "Menü öffnen", closeMenu: "Menü schließen", new: "Neu: {{label}}" }, cl = { dashboard: "Dashboard", setup: "Unternehmenseinrichtung", tutorial: "Erste Schritte", chat: "Chat", reports: "Berichte", "trial-balance": "Rohbilanz", "general-ledger": "Hauptbuch", "stock-balance": "Lagerbestand", "profit-and-loss": "Gewinn- und Verlustrechnung", "balance-sheet": "Bilanz", "ar-aging": "Offene Posten Debitoren", "ap-aging": "Offene Posten Kreditoren", analytics: "Analysen", "chart-of-accounts": "Kontenplan" }, dl = { quotation: { one: "Angebot", other: "Angebote" }, proposal: { one: "Sammelofferte", other: "Sammelofferten" }, "sales-order": { one: "Kundenauftrag", other: "Kundenaufträge" }, "sales-invoice": { one: "Ausgangsrechnung", other: "Ausgangsrechnungen" }, "pos-invoice": { one: "Kassenbeleg", other: "Kassenbelege" }, "purchase-order": { one: "Bestellung", other: "Bestellungen" }, "purchase-invoice": { one: "Eingangsrechnung", other: "Eingangsrechnungen" }, "payment-entry": { one: "Zahlung", other: "Zahlungen" }, "journal-entry": { one: "Buchungssatz", other: "Buchungssätze" }, "bank-transaction": { one: "Banktransaktion", other: "Banktransaktionen" }, "bank-account": { one: "Bankkonto", other: "Bankkonten" }, budget: { one: "Budget", other: "Budgets" }, subscription: { one: "Abonnement", other: "Abonnements" }, "stock-entry": { one: "Lagerbuchung", other: "Lagerbuchungen" }, "delivery-note": { one: "Lieferschein", other: "Lieferscheine" }, "purchase-receipt": { one: "Wareneingang", other: "Wareneingänge" }, "pricing-rule": { one: "Preisregel", other: "Preisregeln" } }, ul = { company: { one: "Unternehmen", other: "Unternehmen" }, customer: { one: "Kunde", other: "Kunden" }, supplier: { one: "Lieferant", other: "Lieferanten" }, item: { one: "Artikel", other: "Artikel" }, warehouse: { one: "Lager", other: "Lager" } }, ml = { chats: "Chats", newChat: "Neuer Chat", deleteChat: "Chat löschen", customAnalytics: "Eigene Analysen", newAnalytics: "Neue Analyse", deleteReport: "Bericht löschen", groups: { Introduction: "Einführung", Selling: "Verkauf", Buying: "Einkauf", Accounting: "Buchhaltung", Stock: "Lager", Reports: "Berichte", Masters: "Stammdaten", Settings: "Einstellungen", Rentals: "Vermietung" }, items: { "Getting Started": "Erste Schritte", "Company Setup": "Unternehmenseinrichtung", "Opening Balances": "Eröffnungsbilanzen", Quotation: "Angebot", Proposal: "Sammelofferte", "Sales Order": "Kundenauftrag", "Sales Invoice": "Ausgangsrechnung", "POS Invoice": "Kassenbeleg", "Purchase Order": "Bestellung", "Purchase Invoice": "Eingangsrechnung", "Payment Entry": "Zahlung", "Journal Entry": "Buchungssatz", "Bank Transaction": "Banktransaktion", "Bank Account": "Bankkonten", "Bank Statement Import": "Kontoauszug importieren", "Bank Reconciliation": "Bankabstimmung", Budget: "Budget", Subscription: "Abonnement", "Stock Entry": "Lagerbuchung", "Delivery Note": "Lieferschein", "Purchase Receipt": "Wareneingang", "Trial Balance": "Rohbilanz", "Profit & Loss": "Gewinn- und Verlustrechnung", "Balance Sheet": "Bilanz", "General Ledger": "Hauptbuch", "AR Aging": "Offene Posten Debitoren", "AP Aging": "Offene Posten Kreditoren", Analytics: "Analysen", "Stock Balance": "Lagerbestand", Company: "Unternehmen", Customer: "Kunde", Supplier: "Lieferant", Item: "Artikel", Warehouse: "Lager", General: "Allgemein", "Pricing Rule": "Preisregel", "Users & Team": "Benutzer & Team", "Chart of Accounts": "Kontenplan", "Fleet Calendar": "Flottenkalender", "Fleet (Assets)": "Flotte (Geräte)", Reservations: "Reservationen" } }, pl = { taglineFirstRun: "Erstellen Sie Ihr Administratorkonto, um zu beginnen", taglineRegister: "Erstellen Sie Ihr Konto", taglineSignIn: "Melden Sie sich bei Ihrem Konto an", demoBanner: "Der Demomodus ist aktiv. Melden Sie sich mit einem Administratorkonto an, um Einstellungen zu verwalten oder den öffentlichen Zugriff zu deaktivieren.", fullName: "Vollständiger Name", email: "E-Mail", password: "Passwort", confirmPassword: "Passwort bestätigen", createAdmin: "Administratorkonto erstellen", register: "Registrieren", signIn: "Anmelden", browsingTitle: "Nur am Stöbern?", browsingBody: "Starten Sie eine neue Chat-Sitzung und sehen Sie sich die Live-Demo an.", enterDemo: "Live-Demo starten", haveInvite: "Haben Sie eine Einladung?", noAccountPrompt: "Noch kein Konto?", registerLink: "Registrieren", alreadyHaveAccount: "Haben Sie bereits ein Konto?", signInLink: "Anmelden", passwordsNoMatch: "Passwörter stimmen nicht überein", passwordTooShort: "Das Passwort muss mindestens 6 Zeichen lang sein", registrationFailed: "Registrierung fehlgeschlagen", loginFailed: "Anmeldung fehlgeschlagen", orContinueWith: "oder weiter mit", continueWithProvider: "Weiter mit {{provider}}", oauthError: { generic: "Anmeldung fehlgeschlagen. Bitte erneut versuchen.", email_exists: "Ein Konto mit dieser E-Mail existiert bereits. Melden Sie sich an und verknüpfen Sie diesen Anbieter in den Kontoeinstellungen.", email_unverified: "Ihr Anbieter hat keine bestätigte E-Mail übermittelt, daher ist keine Anmeldung möglich.", registration_closed: "Für diese E-Mail besteht noch kein Konto und die Registrierung ist nur per Einladung möglich.", invite_invalid: "Diese Einladung ist ungültig, bereits verwendet oder für eine andere E-Mail ausgestellt.", identity_taken: "Dieses Anbieterkonto ist bereits mit einem anderen Benutzer verknüpft.", account_disabled: "Dieses Konto wurde deaktiviert.", token_exchange_failed: "Anmeldung beim Anbieter fehlgeschlagen. Bitte erneut versuchen." } }, hl = { pdfTitle: "PDF & Druck", pageSize: "Seitengröße", pdfHelp: "A4 ist international Standard (210 x 297 mm). Letter ist Standard in den USA (8,5 x 11 Zoll).", passwordTitle: "Passwort ändern", currentPassword: "Aktuelles Passwort", newPassword: "Neues Passwort", confirmNewPassword: "Neues Passwort bestätigen", changePassword: "Passwort ändern", changingPassword: "Wird geändert...", passwordChanged: "Passwort geändert.", setPasswordTitle: "Passwort festlegen", setPasswordBody: "Ihr Konto meldet sich mit Google an. Legen Sie ein Passwort fest, um sich auch mit E-Mail und Passwort anzumelden — ein praktischer Rückfall, falls Sie den Zugang zu Ihrem Google-Konto verlieren.", setPassword: "Passwort festlegen", passwordSet: "Passwort festgelegt.", linkedAccountsTitle: "Verknüpfte Konten", linkedAccountsBody: "Verknüpfen Sie ein Google- oder Apple-Konto, um sich ohne Passwort anzumelden.", linked: "Verknüpft", linkProvider: "{{provider}} verknüpfen", openingTitle: "Eröffnungsbilanzen", openingBody: "Auf der Seite Eröffnungsbilanzen können Kontensalden, Lagerbestände und offene Rechnungen aus einem früheren System importiert werden.", openingEnabled: "Derzeit aktiviert — erreichbar unter Einführung > Eröffnungsbilanzen.", openingDisabled: "Derzeit deaktiviert — die Seite ist in der Seitenleiste ausgeblendet.", signupTitle: "Öffentliche Registrierung", signupBody: "Erlauben Sie es jedem, ohne Einladung ein eigenes Konto (als Betrachter) zu erstellen. Wenn deaktiviert, können neue Benutzer nur über einen Einladungslink beitreten.", signupEnabled: "Derzeit aktiviert — jeder mit der URL kann sich als Betrachter registrieren.", signupDisabled: "Derzeit deaktiviert — die Registrierung erfolgt nur per Einladung.", chatApiTitle: "Chat-API", apiTitle: "Programmatische API", restApiBody: "Apps und Skripten den Zugriff auf dieses ERP über REST oder MCP erlauben — mit API-Schlüsseln oder OAuth-Anmeldung. Standardmässig deaktiviert.", restApiEnabled: "Derzeit aktiviert — ausgestellte Schlüssel können /api/documents, /api/masters und die übrige REST-API aufrufen.", restApiDisabled: "Derzeit deaktiviert — die REST-API akzeptiert nur das Sitzungs-Cookie; Schlüssel werden abgewiesen.", restApiDeactivateTitle: "REST-API deaktivieren?", restApiDeactivateBody: "Der Bearer-Schlüssel-Zugriff auf die REST-API wird abgeschaltet — Konnektoren und Skripte werden abgewiesen, bis Sie ihn wieder einschalten. Die Web-App (Sitzungs-Cookie) ist nicht betroffen, und Ihre API-Schlüssel werden NICHT gelöscht: Sie funktionieren wieder, sobald Sie die API erneut aktivieren.", chatApiBody: "Externen Anwendungen erlauben, über eine HTTP-API mit dem Chat-Agenten dieses ERPs zu sprechen — per Bearer-API-Schlüssel. Standardmäßig deaktiviert.", chatApiEnabled: "Derzeit aktiviert — ausgestellte Schlüssel können POST /api/v1/chat aufrufen.", chatApiDisabled: "Derzeit deaktiviert — die API liefert 404 und Schlüssel funktionieren nicht.", chatApiDeactivateTitle: "Chat-API deaktivieren?", chatApiDeactivateBody: "Die Chat-API wird abgeschaltet — jede programmatische Anfrage (Bearer-API-Schlüssel) wird abgewiesen, bis Sie sie wieder einschalten. Ihre API-Schlüssel werden NICHT gelöscht: Sie bleiben gespeichert und funktionieren wieder, sobald Sie die API erneut aktivieren.", chatApiDeactivateConfirm: "API deaktivieren", chatApiKeysTitle: "Apps und API-Schlüssel", chatApiTokenOnce: "Kopieren Sie diesen Token jetzt — er wird nicht erneut angezeigt.", chatApiCopy: "Kopieren", chatApiDismiss: "Schließen", chatApiRevoked: "widerrufen", chatApiRevoke: "Widerrufen", chatApiNoKeys: "Noch keine Verbindungen.", chatApiName: "Name", chatApiRole: "Rolle", chatApiCreate: "Schlüssel erstellen", chatApiWarn: "Ein Key handelt mit Ihrer Identität und höchstens Ihrer Rolle — wie ein Passwort behandeln. Erst widerrufen, dann löschen.", publicTitle: "Öffentlicher Zugriff (Demomodus)", publicActive: "Aktiv", publicActiveBody: "Der öffentliche Zugriff ist aktiviert. Jeder kann die Anwendung ohne Anmeldung nutzen. Alle Besucher erhalten Manager-Rechte.", disablePublic: "Öffentlichen Zugriff deaktivieren", disabling: "Wird deaktiviert...", publicInactiveBody: "Aktivieren Sie den öffentlichen Zugriff, damit jeder die Anwendung ohne Konto nutzen kann. Nützlich für Demos und Präsentationen. Alle Besucher erhalten Manager-Rechte und teilen sich dieselbe Identität.", enablePublic: "Öffentlichen Zugriff aktivieren", enableWarning: "Dadurch kann jeder ohne Anmeldung auf die Anwendung zugreifen. Besucher können Dokumente erstellen, bearbeiten und buchen. Ihr Administratorkonto erfordert weiterhin eine Anmeldung.", yesEnable: "Ja, aktivieren", enabling: "Wird aktiviert...", tokenSpendTitle: "Token-Ausgaben", chatApiDelete: "Löschen", chatApiYourKeys: "Ihre Verbindungen", chatApiYouBadge: "Sie", chatApiUnknownUser: "Unbekannter Benutzer", chatApiAdminAllNote: "Als Administrator können Sie die Verbindungen aller Benutzer verwalten. Ihre eigenen Verbindungen stehen zuerst.", chatApiConfirmTitle: "Diese Verbindung gehört einem anderen Benutzer", chatApiConfirmRevokeBody: "«{{key}}» gehört {{name}}. Ein Widerruf trennt die App sofort. Fortfahren?", chatApiConfirmDeleteBody: "«{{key}}» gehört {{name}}. Diese widerrufene Verbindung endgültig entfernen?", chatApiPersonal: "Verbinden Sie Apps mit Ihrem ERP-Konto. Der Zugriff überschreitet niemals Ihre aktuellen Berechtigungen.", version: "Version {{version}}" }, gl = { Draft: "Entwurf", Open: "Offen", Submitted: "Gebucht", "To Deliver and Bill": "Zu liefern und abzurechnen", "To Deliver": "Zu liefern", "To Bill": "Abzurechnen", Completed: "Abgeschlossen", Ordered: "Bestellt", Paid: "Bezahlt", Unpaid: "Unbezahlt", Cancelled: "Storniert", Discarded: "Verworfen", Overdue: "Überfällig", Return: "Retoure", Closed: "Geschlossen" }, fl = { periodRange: "Berichtszeitraum: {{from}} – {{to}}", periodFrom: "Berichtszeitraum ab {{from}}", periodThrough: "Berichtszeitraum bis {{to}}", asOfDate: "Stichtag: {{date}}", balanced: "AUSGEGLICHEN", imbalanced: "NICHT AUSGEGLICHEN", income: "Erträge", expenses: "Aufwendungen", totalIncome: "Summe Erträge", totalExpenses: "Summe Aufwendungen", netProfit: "Gewinn", netLoss: "Verlust", assets: "Aktiva", liabilities: "Verbindlichkeiten", equity: "Eigenkapital", totalAssets: "Summe Aktiva", totalLiabilities: "Summe Verbindlichkeiten", totalEquity: "Summe Eigenkapital", totalLiabilitiesEquity: "Summe Verbindlichkeiten + Eigenkapital", current: "Laufend", noData: "Keine Daten gefunden", noEntries: "Keine Buchungen gefunden", noStockData: "Keine Lagerdaten gefunden", noReceivables: "Keine offenen Forderungen", noPayables: "Keine offenen Verbindlichkeiten", noDocuments: "Keine Dokumente gefunden" }, yl = { required: "Erforderlich: {{fields}}", deleteConfirm: "Diesen Datensatz löschen: {{label}}?", disabledNotice: "Dieser Datensatz ({{label}}) ist deaktiviert. Er verbleibt im System, weil andere Datensätze noch darauf verweisen.", disabledOnDelete: "{{label}} {{name}} wurde deaktiviert statt gelöscht, da er von {{reason}} referenziert wird." }, bl = { "Last modified": "Zuletzt geändert", Created: "Erstellt", Account: "Konto", Amount: "Betrag", Balance: "Saldo", Credit: "Haben", Debit: "Soll", Date: "Datum", Party: "Partner", "Party Type": "Partnertyp", "Voucher Type": "Belegart", "Voucher No": "Belegnr.", Invoice: "Rechnung", Customer: "Kunde", Supplier: "Lieferant", "Due Date": "Fälligkeitsdatum", Outstanding: "Offen", "Outstanding Amount": "Offener Betrag", "Item Code": "Artikelnr.", "Item Name": "Artikelname", Item: "Artikel", Warehouse: "Lager", Qty: "Menge", "Valuation Rate": "Bewertungspreis", "Stock Value": "Lagerwert", Name: "Name", Status: "Status", Company: "Unternehmen", "From Date": "Von Datum", "To Date": "Bis Datum", "As of Date": "Stichtag", "Valid Till": "Gültig bis", "Net Total": "Nettobetrag", Tax: "Steuer", "Grand Total": "Gesamtbetrag", Frequency: "Frequenz", "Notes / Terms": "Notizen / Bedingungen", "Posting Date": "Buchungsdatum", "Transaction Date": "Belegdatum", "Delivery Date": "Lieferdatum", "% Delivered": "% geliefert", "% Billed": "% abgerechnet", "Update Stock": "Lager aktualisieren", "Payment Type": "Zahlungsart", "Paid From": "Bezahlt von", "Paid To": "Bezahlt an", "Paid Amount": "Zahlbetrag", Type: "Art", Total: "Gesamt", Allocated: "Zugeordnet", Unallocated: "Nicht zugeordnet", Remark: "Bemerkung", "Total Debit": "Summe Soll", "Total Credit": "Summe Haben", "Cost Center": "Kostenstelle", "Source Warehouse": "Quelllager", "Target Warehouse": "Ziellager", Source: "Quelle", Target: "Ziel", "Incoming Value": "Eingangswert", "Outgoing Value": "Ausgangswert", Rate: "Preis", "Income Account": "Ertragskonto", "Expense Account": "Aufwandskonto", Description: "Beschreibung", "Rate (%)": "Satz (%)", Mode: "Art", Change: "Rückgeld", Currency: "Währung", Title: "Titel", Selling: "Verkauf", Buying: "Einkauf", "Discount %": "Rabatt %", "Discount Amt": "Rabattbetrag", "Min Qty": "Mindestmenge", "Valid From": "Gültig ab", "Valid Upto": "Gültig bis", Priority: "Priorität", Enabled: "Aktiviert", "Fiscal Year": "Geschäftsjahr", "Budget Amount": "Budgetbetrag", "If Exceeded": "Bei Überschreitung", Month: "Monat", "Bank Account": "Bankkonto", Deposit: "Einzahlung", Withdrawal: "Auszahlung", "Reference No": "Referenznr.", "Matched Type": "Zugeordnete Art", "Matched Doc": "Zugeordneter Beleg", "Start Date": "Startdatum", "End Date": "Enddatum", "Billing Interval": "Abrechnungsintervall", "Current Period Start": "Aktueller Periodenbeginn", "Current Period End": "Aktuelles Periodenende", "Plan Items": "Planpositionen", "Total Amount": "Gesamtbetrag", "Stock Entry Type": "Lagerbuchungsart", "Rate Or Discount": "Preis oder Rabatt", "Discount Percentage": "Rabattprozentsatz", ID: "ID", "Customer Name": "Kundenname", "Customer Group": "Kundengruppe", Territory: "Region", "Credit Limit": "Kreditlimit", Email: "E-Mail", Phone: "Telefon", Address: "Adresse", City: "Stadt", "Zip Code": "PLZ", Country: "Land", "Tax ID": "Steuernummer", Website: "Webseite", "Contact Person": "Ansprechperson", "Contact Email": "E-Mail Ansprechperson", "Contact Phone": "Telefon Ansprechperson", "Supplier Name": "Lieferantenname", "Supplier Group": "Lieferantengruppe", "Item Group": "Artikelgruppe", "Stock UOM": "Lagereinheit", "Standard Rate": "Standardpreis", "Warehouse Name": "Lagername", "Company Name": "Firmenname", "Item (type)": "Artikel (Typ)", "Asset Tag / Plate": "Kennung / Kennzeichen", "Home Yard": "Heimdepot", "Purchase Value": "Kaufwert", "Meter Reading": "Zählerstand", "Meter Unit": "Zählereinheit", Yard: "Depot", "Asset (specific unit)": "Gerät (bestimmte Einheit)", Allocation: "Zuweisung", From: "Von", To: "Bis", Purpose: "Zweck", "Purchase Date": "Kaufdatum", Notes: "Notizen" }, vl = { items: "Positionen", taxes: "Steuern", references: "Zahlungsreferenzen", accounts: "Kontobuchungen", payments: "Zahlungen", plans: "Planpositionen", monthly_distribution: "Monatliche Verteilung" }, xl = { "Create Sales Order": "Kundenauftrag erstellen", "Create Sales Invoice": "Ausgangsrechnung erstellen", "Create Delivery Note": "Lieferschein erstellen", "Create Purchase Invoice": "Eingangsrechnung erstellen", "Create Purchase Receipt": "Wareneingang erstellen", "Create Credit Note": "Gutschrift erstellen", "Create Debit Note": "Lastschrift erstellen", "Create Return": "Retoure erstellen" }, kl = { totalRevenue: "Gesamtumsatz", outstandingReceivable: "Offene Forderungen", outstandingPayable: "Offene Verbindlichkeiten", totalStockValue: "Gesamter Lagerwert", recentDocuments: "Letzte Dokumente", noRecentDocuments: "Keine aktuellen Dokumente" }, Nl = { title: "Lambda ERP Chat", subtitle: "Bitten Sie mich, Dokumente zu erstellen, Daten abzurufen oder Berichte auszuführen.", s1: "Welche Kunden haben wir?", s2: "Zeig mir die Rohbilanz", s3: "Erstelle ein Angebot für 10 Bolt Pack M8", s4: "Liste alle unbezahlten Rechnungen auf", loadOlder: "Ältere Nachrichten laden", uploading: "Wird hochgeladen...", failed: "Fehlgeschlagen", remove: "Entfernen", placeholderDemo: "Live-Demo läuft...", placeholderType: "Nachricht eingeben...", placeholderConnecting: "Verbindung wird hergestellt...", attachTitle: "Datei anhängen (PDF oder Bild, max. 10 MB)", creating: "Chat wird erstellt...", dropToAttach: "Datei zum Anhängen ablegen (Bilder oder PDF, je max. 10 MB)", demoError: "Demo konnte nicht gestartet werden", voiceStart: "Sprachnachricht aufnehmen", voiceStop: "Aufnahme stoppen", voiceTranscribing: "Wird transkribiert...", micDenied: "Zugriff auf das Mikrofon wurde verweigert.", transcribeFailed: "Transkription fehlgeschlagen. Bitte erneut versuchen." }, wl = /* @__PURE__ */ JSON.parse('{"pageTitle":"Erste Schritte mit Lambda ERP","intro":"Lambda ERP ist ein KI-natives ERP-System, bei dem die gesamte Funktionalität – etwa das Erstellen von Aufträgen, das Prüfen von Beständen und das Erfassen von Zahlungen – über eine Chat-Oberfläche zugänglich ist. Es leitet an, denkt mit und prüft alle Ihre täglichen Geschäftsaufgaben.","openChat":"KI-Chat öffnen","tipLabel":"Tipp:","topTip":"Der KI-Chat kann Ihnen auch direkte Links zu Dokumenten und herunterladbaren PDFs geben. Bitten Sie ihn einfach, ein Dokument zu erstellen oder nachzuschlagen, und er fügt anklickbare Links in seine Antwort ein.","manualIntro":"Wenn Sie genau verstehen möchten, wie die Dinge unter der Haube funktionieren, oder Dinge lieber manuell erledigen, finden Sie hier einen vollständigen Geschäftszyklus Schritt für Schritt.","caTitle":"Dynamische Analysen – Chatten Sie sich zu jedem Diagramm","caBadge":"Kernfunktion","caBody":"Brauchen Sie einen Bericht, den die Vorlagen nicht liefern? Beschreiben Sie ihn einfach im Chat. Der Assistent ruft ein Code-Spezialmodell auf, das den Bericht für Sie schreibt, ihn live über Ihre ERP-Daten ausführt und einen teilbaren Link zurückgibt. Das Diagramm oder die Tabelle wird in Sekunden gerendert und als Entwurf unter „Eigene Analysen“ in der Seitenleiste gespeichert – Sie können ihn erneut öffnen, die URL mit Kollegen teilen oder den Assistenten bitten, ihn zu verfeinern.","caTryAsking":"Versuchen Sie zu fragen (zum Vorausfüllen im Chat klicken):","caPrompt1":"Zeig mir die Top 10 Kunden nach Umsatz dieses Jahr als Balkendiagramm","caPrompt2":"Monatlicher Einkaufstrend nach Lieferant über alle Monate, x-Achse mit Lieferantennamen beschriften","caPrompt3":"Bestseller-Artikel nach Menge, mit Tabelle und Diagramm","caPrompt4":"Welche Kunden schulden mir gerade am meisten?","caTryInChat":"Im Chat ausprobieren","caOpenWorkspace":"Analyse-Arbeitsbereich öffnen","flowTitle":"Überblick Dokumentenfluss","salesCycle":"Verkaufszyklus","purchaseCycle":"Einkaufszyklus","returns":"Retouren","flowShortcut":"Abkürzung: Das Angebot kann den Kundenauftrag überspringen und direkt zur Ausgangsrechnung oder zum Lieferschein führen","legendStock":"Lagerwirkung","legendGl":"Buchungswirkung","legendCash":"Geldwirkung","legendReversal":"Stornierung","lifecycleTitle":"Dokumenten-Lebenszyklus","lcDraftLabel":"Entwurf:","lcDraftDesc":"Bearbeitbar. Keine finanzielle oder Bestandswirkung.","lcSubmittedLabel":"Gebucht:","lcSubmittedDesc":"Gesperrt. Buchungen und Lagerbuchungen werden erzeugt.","lcCancelledLabel":"Storniert:","lcCancelledDesc":"Alle Buchungen storniert. Das Dokument wird dauerhaft archiviert.","lcNoDelete":"Es gibt kein Löschen. Um einen Entwurf zu verwerfen: buchen und dann stornieren.","lcCorrect":"Um ein gebuchtes Dokument zu korrigieren: stornieren und ein neues erstellen.","readyTitle":"Sie sind startklar","readyBody1":"Sie verstehen jetzt den vollständigen Zyklus. Ein typisches Praxisszenario sieht so aus: Sie erhalten einen Kundenauftrag (Angebot → Kundenauftrag), prüfen den Bestand, kaufen, was Sie brauchen (Bestellung → Wareneingang → Eingangsrechnung → Zahlung), liefern an den Kunden (Lieferschein), stellen ihm die Rechnung (Ausgangsrechnung) und ziehen die Zahlung ein (Zahlung). Kommt etwas zurück, erstellen Sie eine Gutschrift und eine Lieferschein-Retoure, um Finanzen und Bestand umzukehren. Die Rohbilanz bestätigt, dass alles ausgeglichen ist.","readyBody2":"Sie können all dies auch über den KI-Chat per natürlicher Konversation erledigen – beschreiben Sie einfach, was Sie brauchen, und er erstellt die richtigen Dokumente für Sie.","chips":{"Quotation":"Angebot","Sales Order":"Kundenauftrag","Delivery Note":"Lieferschein","Sales Invoice":"Ausgangsrechnung","Payment Entry":"Zahlung","Purchase Order":"Bestellung","Purchase Receipt":"Wareneingang","Purchase Invoice":"Eingangsrechnung","Credit Note":"Gutschrift","DN Return":"Lieferschein-Retoure","Debit Note":"Lastschrift","Draft":"Entwurf","Submitted":"Gebucht","Cancelled":"Storniert"},"links":{"Go to Setup":"Zur Einrichtung","Customers":"Kunden","Suppliers":"Lieferanten","Items":"Artikel","Warehouses":"Lager","New Quotation":"Neues Angebot","View Quotations":"Angebote anzeigen","Stock Balance":"Lagerbestand","New Purchase Order":"Neue Bestellung","Purchase Receipts":"Wareneingänge","Purchase Invoices":"Eingangsrechnungen","Delivery Notes":"Lieferscheine","Sales Invoices":"Ausgangsrechnungen","New Payment Entry":"Neue Zahlung","New Stock Entry":"Neue Lagerbuchung","New Journal Entry":"Neuer Buchungssatz","Profit & Loss":"Gewinn- und Verlustrechnung","Balance Sheet":"Bilanz","AR Aging":"Offene Posten Debitoren","AP Aging":"Offene Posten Kreditoren","Trial Balance":"Rohbilanz","General Ledger":"Hauptbuch","Open AI Chat":"KI-Chat öffnen"},"steps":{"1":{"title":"Unternehmen einrichten","description":"Erstellen Sie ein Unternehmen mit einer Basiswährung. Dadurch wird Ihr Kontenplan generiert (30 Standardkonten in den Bereichen Aktiva, Verbindlichkeiten, Eigenkapital, Erträge und Aufwendungen) sowie eine Standard-Kostenstelle. Sie können außerdem Demodaten mit Beispielkunden, -lieferanten und -artikeln anlegen, um schnell loszulegen."},"2":{"title":"Stammdaten anlegen","description":"Bevor Sie buchen können, benötigen Sie Stammdaten. Legen Sie mindestens einen Kunden, einen Lieferanten, einen Artikel (mit Standardpreis) und ein Lager an. Wenn Sie in Schritt 1 Demodaten angelegt haben, existieren diese bereits.","tip":"Artikel haben einen Standardpreis, der beim Hinzufügen zu Dokumenten automatisch ausgefüllt wird. Sie können den Preis pro Transaktion jederzeit überschreiben."},"3":{"title":"Angebot erstellen","description":"Ein Angebot ist eine unverbindliche Offerte an einen Kunden. Wählen Sie einen Kunden, fügen Sie Positionen mit Mengen und Preisen hinzu, optional Steuerzeilen, und speichern Sie. Angebote haben keine finanzielle Auswirkung – sie erzeugen keine Buchungs- oder Lagerbewegungen. Legen Sie ein Gültigkeitsdatum fest, damit das Angebot automatisch abläuft."},"4":{"title":"Buchen und in Kundenauftrag umwandeln","description":"Öffnen Sie Ihr gespeichertes Angebot und klicken Sie auf Buchen, um es zu bestätigen. Klicken Sie dann auf „Kundenauftrag erstellen“, um es umzuwandeln. Der Kundenauftrag stellt eine bestätigte Zusage des Kunden dar. Er hat weiterhin keine finanzielle Auswirkung, reserviert aber Bestand für die Planung.","tip":"Nur gebuchte Dokumente können in den nächsten Schritt umgewandelt werden. Entwurf → Buchen → Umwandeln ist der Standardablauf. Für schnelle Geschäfte können Sie den Kundenauftrag überspringen und direkt vom Angebot zur Ausgangsrechnung oder zum Lieferschein gehen."},"5":{"title":"Bestand vor der Erfüllung prüfen","description":"Bevor Sie liefern können, prüfen Sie, ob der Artikel tatsächlich auf Lager ist. Öffnen Sie den Bericht Lagerbestand und suchen Sie den Artikel. Wenn Ihr Lager eine Menge von null aufweist, müssen Sie zunächst Bestand beschaffen – entweder über eine Bestellung (Einkauf bei einem Lieferanten) oder eine Lagerbuchung (manueller Eingang).","tip":"Dies ist ein häufiges Szenario aus der Praxis: Sie verkaufen etwas und stellen dann fest, dass Sie es erst einkaufen müssen. Das ERP unterstützt beide Abläufe."},"6":{"title":"Einkaufszyklus: Bestand bei einem Lieferanten kaufen","description":"Wenn Sie Bestand einkaufen müssen, haben Sie zwei gültige Wege. Der Standardweg ist Bestellung → Wareneingang → Eingangsrechnung: Nutzen Sie ihn, wenn die Ware vor oder getrennt von der Lieferantenrechnung eintrifft. Treffen Rechnung und Wareneingang gleichzeitig ein, können Sie die Eingangsrechnung direkt erstellen und „Lager aktualisieren“ aktivieren, sodass dasselbe Dokument sowohl den Bestand vereinnahmt als auch die Verbindlichkeit erfasst.","tip":"Verwenden Sie zuerst den Wareneingang, wenn Sie Waren getrennt erhalten. Verwenden Sie die Eingangsrechnung mit „Lager aktualisieren“, wenn ein Schritt sowohl den Bestand vereinnahmen als auch die Lieferantenrechnung buchen soll. Setzen Sie in diesem direkten Weg ein Lager in jeder Bestandszeile."},"7":{"title":"Lieferschein erstellen","description":"Nachdem Sie nun Bestand haben, kehren Sie zu Ihrem gebuchten Kundenauftrag zurück und klicken Sie auf „Lieferschein erstellen“. Setzen Sie in jeder Position das Lager (von dem die Ware versendet wird) und buchen Sie. Dadurch wird Bestand aus dem Lager ausgebucht – Ihr Lagerbestand sinkt.","tip":"Der Lieferschein ist das Versanddokument. Er reduziert den Bestand, erzeugt aber keine Rechnung. Sie können Lieferung und Rechnungsstellung getrennt vornehmen."},"8":{"title":"Ausgangsrechnung erstellen und buchen","description":"Erstellen Sie aus dem gebuchten Kundenauftrag eine Ausgangsrechnung. Beim Buchen der Rechnung werden Buchungen erzeugt: Forderungen aus Lieferungen und Leistungen werden im Soll gebucht (der Kunde schuldet Ihnen Geld) und Umsatzerlöse im Haben (erzielter Ertrag). Sind Steuern konfiguriert, wird auch die Steuerverbindlichkeit im Haben gebucht. Der offene Betrag zeigt, was der Kunde noch schuldet."},"9":{"title":"Kundenzahlungen erfassen","description":"Erstellen Sie eine Zahlung, um vom Kunden erhaltenes Geld zu erfassen. Setzen Sie die Zahlungsart auf „Receive“ (Erhalten), wählen Sie den Kunden, geben Sie den Betrag an und ordnen Sie ihn der Ausgangsrechnung zu. Sie können Teilzahlungen vornehmen – der offene Betrag der Rechnung aktualisiert sich entsprechend. Erstellen Sie weitere Zahlungen, bis die Rechnung vollständig beglichen ist.","tip":"Teilzahlungen sind üblich. Eine Rechnung über 10.000 kann jetzt mit 3.000 und später mit 7.000 bezahlt werden. Jede Zahlung reduziert den offenen Betrag."},"10":{"title":"Lagerbuchungen (manuelle Bestandsführung)","description":"Verwenden Sie Lagerbuchungen für Bestandsbewegungen, die nicht mit Ein- oder Verkäufen verbunden sind. „Material Receipt“ fügt Bestand hinzu (Eröffnungsbestände, Korrekturen). „Material Issue“ bucht Bestand aus (Abschreibungen, interner Verbrauch). „Material Transfer“ verschiebt Bestand zwischen Lägern. Jede Buchung aktualisiert das Lagerbuch mit gleitender Durchschnittsbewertung.","tip":"Verwenden Sie für eingekaufte Waren Wareneingänge statt Lagerbuchungen – sie sind mit der Bestellung verknüpft und bieten einen sauberen Prüfpfad."},"11":{"title":"Buchungssätze","description":"Buchungssätze sind manuelle buchhalterische Anpassungen – Aufwandsabgrenzungen, Korrekturen, Umgliederungen, Eröffnungsbestände. Jeder Satz muss ausgeglichene Soll- und Haben-Beträge haben (Summe Soll = Summe Haben). Verwenden Sie sie, wenn kein anderer Dokumenttyp passt."},"12":{"title":"Gehaltszahlungen","description":"Lambda ERP wickelt Gehälter über die vorhandenen Buchhaltungswerkzeuge ab. Grenzen Sie zunächst den Gehaltsaufwand ab: Erstellen Sie einen Buchungssatz, der Gehaltsaufwand im Soll und Gehaltsverbindlichkeiten im Haben für den gesamten Lohnbetrag bucht. Bezahlen Sie dann die Mitarbeiter: Erstellen Sie einen weiteren Buchungssatz (oder eine Zahlung), der Gehaltsverbindlichkeiten im Soll und Ihr Bankkonto im Haben bucht. Dieser zweistufige Prozess hält Ihre Bücher korrekt – der Aufwand wird in der richtigen Periode erfasst und der Geldabfluss separat nachverfolgt.","tip":"Sie können den KI-Assistenten bitten, dies für Sie zu erledigen: „Grenze 15.000 an Gehältern für April ab“ gefolgt von „Zahle die April-Gehälter von der Bank“. Er erstellt die richtigen Buchungssätze automatisch."},"13":{"title":"Retouren und Gutschriften","description":"Wenn ein Kunde Waren zurückgibt oder Sie eine Gutschrift ausstellen müssen, erstellen Sie eine Retoure. Retouren verwenden denselben Dokumenttyp mit negativen Mengen. Öffnen Sie eine gebuchte Ausgangsrechnung und erstellen Sie eine Gutschrift (Ausgangsrechnungs-Retoure) – dies kehrt die Buchungen um und reduziert den offenen Betrag der ursprünglichen Rechnung. Erstellen Sie für den Bestand eine Lieferschein-Retoure, um Waren zurück ins Lager zu nehmen. Auf der Einkaufsseite erstellen Sie eine Lastschrift (Eingangsrechnungs-Retoure), um eine Lieferantenrechnung umzukehren, oder eine Wareneingangs-Retoure, um Waren zurückzusenden.","tip":"Eine Gutschrift ist einfach eine Ausgangsrechnung mit is_return=1 und negativen Mengen. Dieselbe Buchungslogik läuft – negative Beträge wechseln automatisch auf die richtige Soll-/Haben-Seite. Für eine vollständige Verkaufsretoure benötigen Sie sowohl eine Gutschrift (Finanzen) als auch eine Lieferschein-Retoure (Bestand)."},"14":{"title":"Berichte ausführen","description":"Prüfen Sie Ihre Bücher. Die Gewinn- und Verlustrechnung zeigt Erträge gegenüber Aufwendungen und den Periodengewinn. Die Bilanz zeigt Ihre finanzielle Lage (Aktiva = Verbindlichkeiten + Eigenkapital). Die Offenen Posten Debitoren zeigen, wer Ihnen Geld schuldet und wie überfällig es ist. Die Offenen Posten Kreditoren zeigen, was Sie Lieferanten schulden. Die Rohbilanz überprüft die doppelte Buchführung. Das Hauptbuch zeigt jede einzelne Buchung. Der Lagerbestand zeigt den aktuellen Bestand."},"15":{"title":"Arbeiten in Fremdwährungen","description":"Ihre Bücher werden in einer einzigen Basiswährung geführt (bei der Unternehmenseinrichtung gewählt), aber Sie können in jeder Währung buchen. Setzen Sie eine Währung auf einer Rechnung oder einem Beleg – oder geben Sie einem Kunden oder Lieferanten eine Standardwährung – und der Wechselkurs für dieses Datum wird automatisch ermittelt und auf dem Dokument gespeichert. Das Dokument behält seine Beträge in seiner eigenen Währung, während das Hauptbuch stets in Ihrer Basiswährung bucht. Wenn Sie später zu einem anderen Kurs einnehmen oder zahlen, wird der realisierte Währungsgewinn oder -verlust automatisch auf ein Konto Währungsgewinn/-verlust gebucht. Sie können sogar ein Fremdwährungs-Bankguthaben halten und es später zum Kurs Ihrer Bank umrechnen – die Differenz zum Buchwert wird dann realisiert. Zum Monatsende können Sie offene Fremdwährungssalden zum Stichtagskurs neu bewerten (ein unrealisierter Gewinn/Verlust, der in der nächsten Periode storniert wird), und Sie können jeden Jahresabschluss zur Anzeige in eine andere Währung umgerechnet betrachten.","tip":"Am einfachsten geht das über den KI-Chat – probieren Sie „Erstelle eine Ausgangsrechnung für Lumiere Audio in EUR“, „Zeig mir die Bilanz in EUR“ oder „Wie hoch ist unser unrealisiertes Währungsrisiko zum Monatsende?“. Die Demodaten enthalten bereits einen EUR-Kunden (Lumiere Audio SARL), dessen Rechnung zu einem anderen Kurs eingenommen wurde, sowie eine offene EUR-Lieferantenrechnung – öffnen Sie das Hauptbuch, um die realisierten Währungsbuchungen zu sehen."}},"setupTitle":"Richten Sie Ihr Unternehmen ein — KI-geführt","setupBadge":"Hier starten","setupBody":"Sagen Sie dem Assistenten, welche Art von Unternehmen Sie führen, und er erstellt Ihren Kontenplan für Sie — zugeschnitten auf Ihre Branche, mit den passenden Konten und sinnvollen Voreinstellungen. Er erklärt jede Entscheidung und legt erst dann etwas an, wenn Sie zustimmen.","setupSectors":"Zugeschnitten auf Dienstleistungen, Einzelhandel & POS, Gastronomie, Großhandel & Distribution, Import/Export, Fertigung und Bauwesen.","setupGetStarted":"Im Chat loslegen","setupManual":"Lieber ein manuelles Formular? Nutzen Sie die klassische Einrichtung.","setupManualLink":"Klassische Einrichtung","setupPrompt":"Ich möchte mein Unternehmen einrichten. Können Sie mich durch den Prozess führen?"}'), Cl = { title: "Formatierungshilfe", intro: "Der Text unter Notizen / Konditionen unterstützt eine einfache Formatierung im PDF:", heading: "fette Überschrift", emphasis: "kursiv / fett", rule: "horizontale Trennlinie", price: "rechtsbündiger Preis neben dem Text darüber (z. B. Monatlich | CHF 380.—)", block: "Trennen Sie Blöcke durch eine Leerzeile." }, Sl = { period: "Periode", allTime: "Gesamter Zeitraum", semantics: "Bilanzkonten: Saldo per Periodenende. Erfolgskonten: Bewegung innerhalb der Periode. Konto anklicken für den Kontoauszug.", empty: "Keine Konten gefunden. Zuerst die Firmeneinrichtung abschliessen.", disabled: "deaktiviert", rootType: { Asset: "Aktiven", Liability: "Passiven", Equity: "Eigenkapital", Income: "Ertrag", Expense: "Aufwand" } }, Al = { fleetCalendar: "Flottenkalender", yard: "Depot", machineType: "Maschinentyp", prev: "Zurück", today: "Heute", next: "Weiter", weeks: "Wochen", newBooking: "Neue Reservation", reserved: "Reserviert", onHire: "Vermietet", machine: "Maschine", loadError: "Flottenkalender konnte nicht geladen werden.", noAssets: "Keine Geräte vorhanden. Maschinen unter Vermietung → Flotte hinzufügen.", pooledNote: "{{count}} Pool-Reservation(en) noch ohne Maschinenzuordnung", unassigned: "Pool — noch nicht zugeordnet", poolWarning: "Die Pool-Kapazität ist reserviert. Eine konkrete Maschine ist noch nicht zugeordnet; vor der Ausgabe eine Maschine auswählen." }, _l = { Unit: "Bestimmte Maschine", Pool: "Pool – Maschine später zuweisen", Available: "Verfügbar", "On Hire": "Vermietet", Out: "Ausgeliefert", Maintenance: "Wartung", Retired: "Ausgemustert", Reserved: "Reserviert", Returned: "Zurückgegeben", Cancelled: "Storniert", Customer: "Kunde", Supplier: "Lieferant" }, Dl = { asset: { item_code: "Der Artikel ist der Maschinen-TYP und trägt den Tarif; ein Gerät ist eine physische Einheit davon. Der Artikel muss is_asset_tracked = 1 haben.", asset_tag: "Kennzeichen oder Seriennummer — muss über alle Geräte eindeutig sein.", status: "Aktueller Zustand der Einheit (kein Zeitraum). Ausgemusterte Einheiten verlassen den Pool.", meter_reading: "Betriebsstunden / km." }, reservation: { item_code: "Pool-Reservation ('ein 1.7t-Bagger'): Artikel + Depot + Menge setzen. Für eine bestimmte Maschine stattdessen Gerät setzen.", asset: "Eine bestimmte Maschine buchen. Eine Einheiten-Buchung belegt auch einen Platz im Artikel+Depot-Pool.", from_datetime: "Genauer Beginn — Datum und Uhrzeit (z. B. 14:27). Ein reines Datum bedeutet 00:00.", to_datetime: "Genaues Ende. Fenster sind halboffen [von, bis): eine Miete, die um 09:00 endet, und die nächste um 09:00 beginnende, kollidieren NICHT." } }, Pl = { label: "Standard-Verkaufssteuer", none: "Keine automatische Übernahme", hint: "Wird bei neuen Offerten, Aufträgen und Rechnungen übernommen, sofern keine Steuern ausdrücklich vorgegeben werden. Bestehende Belege bleiben unverändert. Mit „Speichern“ übernehmen.", saveFirst: "Firma zuerst speichern. Anschliessend können Sie eine ihrer Verkaufsteuervorlagen auswählen.", loadError: "Steuervorlagen konnten nicht geladen werden. Die gespeicherte Auswahl bleibt erhalten.", retry: "Erneut versuchen", unavailable: "Die gespeicherte Vorlage ist für diese Firma nicht als Verkaufsteuervorlage verfügbar. Bitte prüfen Sie die Auswahl.", empty: "Für diese Firma sind noch keine Verkaufsteuervorlagen hinterlegt." }, Tl = { title: "App verbinden", subtitle: "Wählen Sie, wo Sie Lambda ERP nutzen möchten. Wir zeigen Ihnen die passende Einrichtung.", other: "Etwas anderes", customName: "Eigene Integration", signIn: "Anmelden", apiKey: "API-Schlüssel", access: "Zugriff", role_viewer: "Daten ansehen", role_manager: "Erstellen und bearbeiten", role_admin: "Voller administrativer Zugriff", roleHelp: "Der Zugriff ist immer durch Ihre eigenen ERP-Berechtigungen begrenzt. Wählen Sie nur, was diese App benötigt.", copied: "Kopiert", copyFailed: "Automatisches Kopieren fehlgeschlagen. Markieren und kopieren Sie den Text manuell.", retry: "Erneut versuchen", interface: "Verbindungsart", chatDisabled: "Der Chat-API-Zugriff ist deaktiviert. Ein Administrator kann ihn oben in den API-Einstellungen aktivieren.", restDisabled: "Der REST-API-/MCP-Zugriff ist deaktiviert. Ein Administrator muss ihn in den API-Einstellungen aktivieren, bevor sich diese App verbinden kann.", chatgptStep: "Fügen Sie in ChatGPT eine eigene App / einen Konnektor hinzu. Entwicklermodus und eine Workspace-Berechtigung können erforderlich sein.", claudeStep: "Öffnen Sie in Claude «Connectors» und wählen Sie «Add custom connector».", urlStep: "Fügen Sie diese Server-URL ein. Verwenden Sie die OAuth-Anmeldung; lassen Sie optionale Felder für Client-ID und Client-Secret leer.", approveStep: "Verbinden Sie die App, melden Sie sich bei Lambda ERP an und bestätigen Sie den gewünschten Zugriff.", noKeyNeeded: "Sie benötigen weder API-Schlüssel noch eigene Header. Den Zugriff wählen Sie auf der Bestätigungsseite.", officialGuide: "Einrichtungsanleitung der App öffnen", copyUrl: "Server-URL kopieren", keyReady: "Ihr Verbindungsschlüssel ist bereit", copySetup: "Einrichtung kopieren", copyHeader: "Autorisierungsheader kopieren", cliStep: "Führen Sie diesen Befehl im Terminal aus. Prüfen Sie die Verbindung anschliessend mit /mcp in Claude Code.", codexStep: "Fügen Sie diesen Eintrag Ihrer Codex-Konfiguration hinzu. Starten Sie Codex neu und prüfen Sie /mcp.", done: "Fertig — Schlüssel ausblenden", existing: "Bestehende Verbindungen", refresh: "Aktualisieren", lastUsed: "Zuletzt verwendet", notUsed: "Noch nicht verwendet", consentTitle: "Mit Lambda ERP verbinden", expired: "Diese Verbindungsanfrage fehlt oder ist abgelaufen. Starten Sie erneut in Ihrer App.", loginFirst: "Melden Sie sich bei Ihrem ERP-Konto an, um die Anfrage dieser App zu prüfen.", consentBody: "«{{app}}» möchte auf Ihr ERP zugreifen. Wählen Sie den Zugriff, den Sie erlauben möchten.", returnTo: "Nach der Bestätigung kehren Sie hierhin zurück", revokeHelp: "Sie können diese Verbindung jederzeit unter Einstellungen → API-Zugriff widerrufen.", deny: "Abbrechen", allow: "Verbindung erlauben", explain_viewer: "Die App kann ERP-Datensätze und Berichte lesen sowie Beleg-PDFs erstellen. Sie kann keine Geschäftsdaten bearbeiten.", explain_manager: "Die App kann zusätzlich Belege erstellen, bearbeiten, buchen und stornieren sowie Stammdaten aktualisieren.", explain_admin: "Die App kann alle verfügbaren ERP-Werkzeuge nutzen, einschliesslich administrativer Aktionen und dem Löschen von Stammdaten." }, Rl = {
8146
+ }, tl = { incomplete: "Entwurf gespeichert. Für das PDF werden Kunde, Unternehmen und mindestens ein Angebot benötigt." }, nl = { save: "Speichern", saving: "Speichern...", submit: "Buchen", cancel: "Abbrechen", cancelDoc: "Stornieren", cancelDocAbort: "Nicht stornieren", cancelDocConfirmBtn: "Definitiv stornieren", cancelBooking: "Buchung stornieren", cancelBookingConfirm: "Ja, stornieren", keepBooking: "Behalten", discardDraft: "Entwurf verwerfen", keepDraft: "Behalten", discardDraftConfirm: "Verwerfen", showDiscarded: "Verworfene anzeigen", showDisabled: "Deaktivierte anzeigen", columns: "Spalten", searchPlaceholder: "Suchen…", noRecords: "Keine Einträge gefunden", active: "Aktiv", disabled: "Deaktiviert", sortByColumn: "Nach dieser Spalte sortieren", delete: "Löschen", edit: "Bearbeiten", pdf: "PDF", addRow: "Zeile hinzufügen", loading: "Lädt...", enable: "Aktivieren", disable: "Deaktivieren", refresh: "Aktualisieren", refreshing: "Aktualisiert...", areYouSure: "Sind Sie sicher?", all: "Alle", apply: "Anwenden", new: "Neu", prev: "Zurück", next: "Weiter", perPage: "Pro Seite", page: "Seite", of: "von", showing: "Zeige", total: "Gesamt", totals: "Summen", errorOccurred: "Ein Fehler ist aufgetreten" }, al = { today: "Heute", thisWeek: "Diese Woche", thisMonth: "Dieser Monat", thisQuarter: "Dieses Quartal", thisYear: "Dieses Jahr", lastMonth: "Letzter Monat", lastQuarter: "Letztes Quartal", lastYear: "Letztes Jahr", endOfThisMonth: "Ende dieses Monats", endOfLastMonth: "Ende des letzten Monats", endOfThisQuarter: "Ende dieses Quartals", endOfThisYear: "Ende dieses Jahres", endOfLastYear: "Ende des letzten Jahres" }, rl = { title: "Kontoauszug importieren", subtitle: "ISO-20022-CAMT-Kontoauszüge prüfen und ohne automatische Verbuchung importieren.", selectFiles: "CAMT-Dateien", chooseFiles: "XML- oder ZIP-Dateien auswählen", dropFiles: "XML- oder ZIP-Dateien hier ablegen", unsupportedFiles: "Es werden nur CAMT-XML- oder ZIP-Dateien unterstützt: {{files}}", fileHint: "camt.053.001.04 und camt.053.001.08 · mehrere Dateien möglich", preview: "Vorschau anzeigen", noPosting: "Vorschau und Import erzeugen keine Buchungssätze.", alreadyImported: "Bereits importiert", period: "Zeitraum", openingBalance: "Anfangssaldo", closingBalance: "Endsaldo", entries: "Buchungen", credits: "Gutschriften", debits: "Belastungen", details: "Transaktionsdetails", batches: "Sammelbuchungen", qrReferences: "QR-Referenzen", mapping: "Bankkonto-Zuordnung", selectBankAccount: "Bankkonto auswählen…", noBankAccounts: "Es wurde noch kein Bankkonto eingerichtet.", createBankAccount: "Bankkonto einrichten", mappingRequired: "Ordnen Sie vor dem Import jeden neuen Auszug einem Bankkonto zu.", import: "Kontoauszüge importieren", importComplete: "Import abgeschlossen", importCounts: "{{imported}} Buchungen importiert, {{duplicates}} Duplikate übersprungen", history: "Letzte Importe", importId: "Import", bankAccount: "Bankkonto", source: "Quelldatei", openReconciliation: "Bankabstimmung öffnen" }, il = { title: "Bankabstimmung", subtitle: "Importierte Banktransaktionen mit Buchungen, offenen Rechnungen oder Sachkonten abstimmen.", status: "Status", unreconciled: "Nicht abgestimmt", reconciled: "Abgestimmt", transactions: "Banktransaktionen", empty: "In dieser Ansicht sind keine Banktransaktionen vorhanden.", remittance: "Zahlungsinformation", reference: "Referenz", openTransaction: "Banktransaktion öffnen", conversionRate: "Wechselkurs (1 {{currency}} in {{base}})", conversionRateHint: "Erforderlich, wenn für das Transaktionsdatum kein Wechselkurs hinterlegt ist.", existingVouchers: "Bestehende Buchungen", existingVouchersHint: "Diese eingereichten Belege enthalten eine exakt passende Bewegung auf diesem Bankkonto. Bei der Zuordnung wird nichts erneut gebucht.", match: "Zuordnen", openInvoices: "Offene Rechnungen", openInvoicesHint: "Wähle eine oder mehrere Rechnungen derselben Partei. Nach der Bestätigung wird eine Zahlung erstellt und eingereicht.", outstanding: "Offen", allocation: "Zuweisung", noInvoiceSuggestions: "Es wurde keine plausible offene Rechnung gefunden.", allocated: "{{amount}} von {{total}} zugewiesen", createPayment: "Zahlung erstellen und abstimmen", directPosting: "Auf ein Sachkonto buchen", directPostingHint: "Für Gebühren, Zinsen und andere Bewegungen ohne Rechnung. Debitoren-, Kreditoren-, Bank- und Kassenkonten sind bewusst ausgeschlossen.", counterpartyAccount: "Gegenkonto", remarks: "Bemerkung", createJournal: "Buchungssatz erstellen und abstimmen", activeMatch: "Aktive Abstimmung", undo: "Abstimmung rückgängig machen", confirmExistingTitle: "Bestehende Buchung zuordnen?", confirmExistingBody: "{{transaction}} mit {{voucher}} abstimmen? Es wird kein neuer Buchungssatz erzeugt.", confirmGroupBody: "Alle {{count}} Transaktionen über insgesamt {{amount}} mit {{voucher}} abstimmen? Es wird kein neuer Buchungssatz erzeugt.", groupedTransactions: "{{count}} gruppierte Transaktionen", confirmPaymentTitle: "Zahlung buchen und abstimmen?", confirmPaymentBody: "Damit wird für {{transaction}} eine Zahlung über {{amount}} eingereicht und auf {{count}} Rechnung(en) verteilt.", onAccountWarning: "{{amount}} bleibt als nicht zugewiesene Zahlung bei dieser Partei bestehen.", confirmJournalTitle: "Buchungssatz erstellen und abstimmen?", confirmJournalBody: "Damit werden {{amount}} für {{transaction}} gegen {{account}} gebucht.", postAndReconcile: "Buchen und abstimmen", undoTitle: "Abstimmung rückgängig machen?", undoCreatedBody: "Der durch diese Abstimmung erstellte Beleg wird storniert, seine Hauptbuchungen werden umgekehrt und Rechnungszuweisungen wiederhergestellt.", undoExistingBody: "Der bestehende Beleg wird nur von der Banktransaktion getrennt. Er bleibt eingereicht und seine Buchungen werden nicht verändert.", undoExistingGroupBody: "Alle {{count}} gruppierten Banktransaktionen ({{transactions}}) werden gemeinsam vom Beleg getrennt. Der bestehende Beleg bleibt eingereicht und seine Buchungen werden nicht verändert.", reconciledNotice: "Banktransaktion wurde abgestimmt.", groupReconciledNotice: "{{count}} Banktransaktionen wurden als Gruppe abgestimmt.", undoneNotice: "Abstimmung wurde rückgängig gemacht." }, sl = { placeholder: "Suchen oder Feld:Wert eingeben…", search: "Suchen", addFilter: "+ Filter", availableFields: "Verfügbare Felder", fieldHelp: "Feld auswählen oder den technischen Namen mit Doppelpunkt eingeben.", field: "Feld", valueFor: "Wert für {{label}}", allFields: "Alle Felder", typeForValues: "Tippen, um Werte in diesem Feld zu finden.", noValues: "Keine passenden Werte gefunden.", noFields: "Keine passenden Felder gefunden.", useValue: "„{{value}}“ verwenden", removeFilter: "Filter {{label}} entfernen", types: { text: "Text", textarea: "Langtext", select: "Auswahl", link: "Verknüpfter Eintrag", date: "Datum", datetime: "Datum und Uhrzeit", number: "Zahl", currency: "Betrag" } }, ol = { title: "Sprache", help: "Wählen Sie die Sprache der Oberfläche. Ihre Auswahl wird in diesem Browser gespeichert.", label: "Sprache" }, ll = { back: "Zurück", logout: "Abmelden", openMenu: "Menü öffnen", closeMenu: "Menü schließen", new: "Neu: {{label}}" }, cl = { dashboard: "Dashboard", setup: "Unternehmenseinrichtung", tutorial: "Erste Schritte", chat: "Chat", reports: "Berichte", "trial-balance": "Rohbilanz", "general-ledger": "Hauptbuch", "stock-balance": "Lagerbestand", "profit-and-loss": "Gewinn- und Verlustrechnung", "balance-sheet": "Bilanz", "ar-aging": "Offene Posten Debitoren", "ap-aging": "Offene Posten Kreditoren", analytics: "Analysen", "chart-of-accounts": "Kontenplan" }, dl = { quotation: { one: "Angebot", other: "Angebote" }, proposal: { one: "Sammelofferte", other: "Sammelofferten" }, "sales-order": { one: "Kundenauftrag", other: "Kundenaufträge" }, "sales-invoice": { one: "Ausgangsrechnung", other: "Ausgangsrechnungen" }, "pos-invoice": { one: "Kassenbeleg", other: "Kassenbelege" }, "purchase-order": { one: "Bestellung", other: "Bestellungen" }, "purchase-invoice": { one: "Eingangsrechnung", other: "Eingangsrechnungen" }, "payment-entry": { one: "Zahlung", other: "Zahlungen" }, "journal-entry": { one: "Buchungssatz", other: "Buchungssätze" }, "bank-transaction": { one: "Banktransaktion", other: "Banktransaktionen" }, "bank-account": { one: "Bankkonto", other: "Bankkonten" }, budget: { one: "Budget", other: "Budgets" }, subscription: { one: "Abonnement", other: "Abonnements" }, "stock-entry": { one: "Lagerbuchung", other: "Lagerbuchungen" }, "delivery-note": { one: "Lieferschein", other: "Lieferscheine" }, "purchase-receipt": { one: "Wareneingang", other: "Wareneingänge" }, "pricing-rule": { one: "Preisregel", other: "Preisregeln" } }, ul = { company: { one: "Unternehmen", other: "Unternehmen" }, customer: { one: "Kunde", other: "Kunden" }, supplier: { one: "Lieferant", other: "Lieferanten" }, item: { one: "Artikel", other: "Artikel" }, warehouse: { one: "Lager", other: "Lager" } }, ml = { chats: "Chats", newChat: "Neuer Chat", deleteChat: "Chat löschen", customAnalytics: "Eigene Analysen", newAnalytics: "Neue Analyse", deleteReport: "Bericht löschen", groups: { Introduction: "Einführung", Selling: "Verkauf", Buying: "Einkauf", Accounting: "Buchhaltung", Stock: "Lager", Reports: "Berichte", Masters: "Stammdaten", Settings: "Einstellungen", Rentals: "Vermietung" }, items: { "Getting Started": "Erste Schritte", "Company Setup": "Unternehmenseinrichtung", "Opening Balances": "Eröffnungsbilanzen", Quotation: "Angebot", Proposal: "Sammelofferte", "Sales Order": "Kundenauftrag", "Sales Invoice": "Ausgangsrechnung", "POS Invoice": "Kassenbeleg", "Purchase Order": "Bestellung", "Purchase Invoice": "Eingangsrechnung", "Payment Entry": "Zahlung", "Journal Entry": "Buchungssatz", "Bank Transaction": "Banktransaktion", "Bank Account": "Bankkonten", "Bank Statement Import": "Kontoauszug importieren", "Bank Reconciliation": "Bankabstimmung", Budget: "Budget", Subscription: "Abonnement", "Stock Entry": "Lagerbuchung", "Delivery Note": "Lieferschein", "Purchase Receipt": "Wareneingang", "Trial Balance": "Rohbilanz", "Profit & Loss": "Gewinn- und Verlustrechnung", "Balance Sheet": "Bilanz", "General Ledger": "Hauptbuch", "AR Aging": "Offene Posten Debitoren", "AP Aging": "Offene Posten Kreditoren", Analytics: "Analysen", "Stock Balance": "Lagerbestand", Company: "Unternehmen", Customer: "Kunde", Supplier: "Lieferant", Item: "Artikel", Warehouse: "Lager", General: "Allgemein", "Pricing Rule": "Preisregel", "Users & Team": "Benutzer & Team", "Chart of Accounts": "Kontenplan", "Fleet Calendar": "Flottenkalender", "Fleet (Assets)": "Flotte (Geräte)", Reservations: "Reservationen" } }, pl = { taglineFirstRun: "Erstellen Sie Ihr Administratorkonto, um zu beginnen", taglineRegister: "Erstellen Sie Ihr Konto", taglineSignIn: "Melden Sie sich bei Ihrem Konto an", demoBanner: "Der Demomodus ist aktiv. Melden Sie sich mit einem Administratorkonto an, um Einstellungen zu verwalten oder den öffentlichen Zugriff zu deaktivieren.", fullName: "Vollständiger Name", email: "E-Mail", password: "Passwort", confirmPassword: "Passwort bestätigen", createAdmin: "Administratorkonto erstellen", register: "Registrieren", signIn: "Anmelden", browsingTitle: "Nur am Stöbern?", browsingBody: "Starten Sie eine neue Chat-Sitzung und sehen Sie sich die Live-Demo an.", enterDemo: "Live-Demo starten", haveInvite: "Haben Sie eine Einladung?", noAccountPrompt: "Noch kein Konto?", registerLink: "Registrieren", alreadyHaveAccount: "Haben Sie bereits ein Konto?", signInLink: "Anmelden", passwordsNoMatch: "Passwörter stimmen nicht überein", passwordTooShort: "Das Passwort muss mindestens 6 Zeichen lang sein", registrationFailed: "Registrierung fehlgeschlagen", loginFailed: "Anmeldung fehlgeschlagen", orContinueWith: "oder weiter mit", continueWithProvider: "Weiter mit {{provider}}", oauthError: { generic: "Anmeldung fehlgeschlagen. Bitte erneut versuchen.", email_exists: "Ein Konto mit dieser E-Mail existiert bereits. Melden Sie sich an und verknüpfen Sie diesen Anbieter in den Kontoeinstellungen.", email_unverified: "Ihr Anbieter hat keine bestätigte E-Mail übermittelt, daher ist keine Anmeldung möglich.", registration_closed: "Für diese E-Mail besteht noch kein Konto und die Registrierung ist nur per Einladung möglich.", invite_invalid: "Diese Einladung ist ungültig, bereits verwendet oder für eine andere E-Mail ausgestellt.", identity_taken: "Dieses Anbieterkonto ist bereits mit einem anderen Benutzer verknüpft.", account_disabled: "Dieses Konto wurde deaktiviert.", token_exchange_failed: "Anmeldung beim Anbieter fehlgeschlagen. Bitte erneut versuchen." } }, hl = { pdfTitle: "PDF & Druck", pageSize: "Seitengröße", pdfHelp: "A4 ist international Standard (210 x 297 mm). Letter ist Standard in den USA (8,5 x 11 Zoll).", passwordTitle: "Passwort ändern", currentPassword: "Aktuelles Passwort", newPassword: "Neues Passwort", confirmNewPassword: "Neues Passwort bestätigen", changePassword: "Passwort ändern", changingPassword: "Wird geändert...", passwordChanged: "Passwort geändert.", setPasswordTitle: "Passwort festlegen", setPasswordBody: "Ihr Konto meldet sich mit Google an. Legen Sie ein Passwort fest, um sich auch mit E-Mail und Passwort anzumelden — ein praktischer Rückfall, falls Sie den Zugang zu Ihrem Google-Konto verlieren.", setPassword: "Passwort festlegen", passwordSet: "Passwort festgelegt.", linkedAccountsTitle: "Verknüpfte Konten", linkedAccountsBody: "Verknüpfen Sie ein Google- oder Apple-Konto, um sich ohne Passwort anzumelden.", linked: "Verknüpft", linkProvider: "{{provider}} verknüpfen", openingTitle: "Eröffnungsbilanzen", openingBody: "Auf der Seite Eröffnungsbilanzen können Kontensalden, Lagerbestände und offene Rechnungen aus einem früheren System importiert werden.", openingEnabled: "Derzeit aktiviert — erreichbar unter Einführung > Eröffnungsbilanzen.", openingDisabled: "Derzeit deaktiviert — die Seite ist in der Seitenleiste ausgeblendet.", signupTitle: "Öffentliche Registrierung", signupBody: "Erlauben Sie es jedem, ohne Einladung ein eigenes Konto (als Betrachter) zu erstellen. Wenn deaktiviert, können neue Benutzer nur über einen Einladungslink beitreten.", signupEnabled: "Derzeit aktiviert — jeder mit der URL kann sich als Betrachter registrieren.", signupDisabled: "Derzeit deaktiviert — die Registrierung erfolgt nur per Einladung.", chatApiTitle: "Chat-API", apiTitle: "Programmatische API", restApiBody: "Apps und Skripten den Zugriff auf dieses ERP über REST oder MCP erlauben — mit API-Schlüsseln oder OAuth-Anmeldung. Standardmässig deaktiviert.", restApiEnabled: "Derzeit aktiviert — ausgestellte Schlüssel können /api/documents, /api/masters und die übrige REST-API aufrufen.", restApiDisabled: "Derzeit deaktiviert — die REST-API akzeptiert nur das Sitzungs-Cookie; Schlüssel werden abgewiesen.", restApiDeactivateTitle: "REST-API deaktivieren?", restApiDeactivateBody: "Der Bearer-Schlüssel-Zugriff auf die REST-API wird abgeschaltet — Konnektoren und Skripte werden abgewiesen, bis Sie ihn wieder einschalten. Die Web-App (Sitzungs-Cookie) ist nicht betroffen, und Ihre API-Schlüssel werden NICHT gelöscht: Sie funktionieren wieder, sobald Sie die API erneut aktivieren.", chatApiBody: "Externen Anwendungen erlauben, über eine HTTP-API mit dem Chat-Agenten dieses ERPs zu sprechen — per Bearer-API-Schlüssel. Standardmäßig deaktiviert.", chatApiEnabled: "Derzeit aktiviert — ausgestellte Schlüssel können POST /api/v1/chat aufrufen.", chatApiDisabled: "Derzeit deaktiviert — die API liefert 404 und Schlüssel funktionieren nicht.", chatApiDeactivateTitle: "Chat-API deaktivieren?", chatApiDeactivateBody: "Die Chat-API wird abgeschaltet — jede programmatische Anfrage (Bearer-API-Schlüssel) wird abgewiesen, bis Sie sie wieder einschalten. Ihre API-Schlüssel werden NICHT gelöscht: Sie bleiben gespeichert und funktionieren wieder, sobald Sie die API erneut aktivieren.", chatApiDeactivateConfirm: "API deaktivieren", chatApiKeysTitle: "Apps und API-Schlüssel", chatApiTokenOnce: "Kopieren Sie diesen Token jetzt — er wird nicht erneut angezeigt.", chatApiCopy: "Kopieren", chatApiDismiss: "Schließen", chatApiRevoked: "widerrufen", chatApiRevoke: "Widerrufen", chatApiNoKeys: "Noch keine Verbindungen.", chatApiName: "Name", chatApiRole: "Rolle", chatApiCreate: "Schlüssel erstellen", chatApiWarn: "Ein Key handelt mit Ihrer Identität und höchstens Ihrer Rolle — wie ein Passwort behandeln. Erst widerrufen, dann löschen.", publicTitle: "Öffentlicher Zugriff (Demomodus)", publicActive: "Aktiv", publicActiveBody: "Der öffentliche Zugriff ist aktiviert. Jeder kann die Anwendung ohne Anmeldung nutzen. Alle Besucher erhalten Manager-Rechte.", disablePublic: "Öffentlichen Zugriff deaktivieren", disabling: "Wird deaktiviert...", publicInactiveBody: "Aktivieren Sie den öffentlichen Zugriff, damit jeder die Anwendung ohne Konto nutzen kann. Nützlich für Demos und Präsentationen. Alle Besucher erhalten Manager-Rechte und teilen sich dieselbe Identität.", enablePublic: "Öffentlichen Zugriff aktivieren", enableWarning: "Dadurch kann jeder ohne Anmeldung auf die Anwendung zugreifen. Besucher können Dokumente erstellen, bearbeiten und buchen. Ihr Administratorkonto erfordert weiterhin eine Anmeldung.", yesEnable: "Ja, aktivieren", enabling: "Wird aktiviert...", tokenSpendTitle: "Token-Ausgaben", chatApiDelete: "Löschen", chatApiYourKeys: "Ihre Verbindungen", chatApiYouBadge: "Sie", chatApiUnknownUser: "Unbekannter Benutzer", chatApiAdminAllNote: "Als Administrator können Sie die Verbindungen aller Benutzer verwalten. Ihre eigenen Verbindungen stehen zuerst.", chatApiConfirmTitle: "Diese Verbindung gehört einem anderen Benutzer", chatApiConfirmRevokeBody: "«{{key}}» gehört {{name}}. Ein Widerruf trennt die App sofort. Fortfahren?", chatApiConfirmDeleteBody: "«{{key}}» gehört {{name}}. Diese widerrufene Verbindung endgültig entfernen?", chatApiPersonal: "Verbinden Sie Apps mit Ihrem ERP-Konto. Der Zugriff überschreitet niemals Ihre aktuellen Berechtigungen.", version: "Version {{version}}" }, gl = { Draft: "Entwurf", Open: "Offen", Submitted: "Gebucht", "To Deliver and Bill": "Zu liefern und abzurechnen", "To Deliver": "Zu liefern", "To Bill": "Abzurechnen", Completed: "Abgeschlossen", Ordered: "Bestellt", Paid: "Bezahlt", Unpaid: "Unbezahlt", Cancelled: "Storniert", Discarded: "Verworfen", Overdue: "Überfällig", Return: "Retoure", Closed: "Geschlossen" }, fl = { periodRange: "Berichtszeitraum: {{from}} – {{to}}", periodFrom: "Berichtszeitraum ab {{from}}", periodThrough: "Berichtszeitraum bis {{to}}", asOfDate: "Stichtag: {{date}}", balanced: "AUSGEGLICHEN", imbalanced: "NICHT AUSGEGLICHEN", income: "Erträge", expenses: "Aufwendungen", totalIncome: "Summe Erträge", totalExpenses: "Summe Aufwendungen", netProfit: "Gewinn", netLoss: "Verlust", assets: "Aktiva", liabilities: "Verbindlichkeiten", equity: "Eigenkapital", totalAssets: "Summe Aktiva", totalLiabilities: "Summe Verbindlichkeiten", totalEquity: "Summe Eigenkapital", totalLiabilitiesEquity: "Summe Verbindlichkeiten + Eigenkapital", current: "Laufend", noData: "Keine Daten gefunden", noEntries: "Keine Buchungen gefunden", noStockData: "Keine Lagerdaten gefunden", noReceivables: "Keine offenen Forderungen", noPayables: "Keine offenen Verbindlichkeiten", noDocuments: "Keine Dokumente gefunden" }, yl = { required: "Erforderlich: {{fields}}", deleteConfirm: "Diesen Datensatz löschen: {{label}}?", disabledNotice: "Dieser Datensatz ({{label}}) ist deaktiviert. Er verbleibt im System, weil andere Datensätze noch darauf verweisen.", disabledOnDelete: "{{label}} {{name}} wurde deaktiviert statt gelöscht, da er von {{reason}} referenziert wird." }, bl = { "Last modified": "Zuletzt geändert", Created: "Erstellt", Account: "Konto", Amount: "Betrag", Balance: "Saldo", Credit: "Haben", Debit: "Soll", Date: "Datum", Party: "Partner", "Party Type": "Partnertyp", "Voucher Type": "Belegart", "Voucher No": "Belegnr.", Invoice: "Rechnung", Customer: "Kunde", Supplier: "Lieferant", "Due Date": "Fälligkeitsdatum", Outstanding: "Offen", "Outstanding Amount": "Offener Betrag", "Item Code": "Artikelnr.", "Item Name": "Artikelname", Item: "Artikel", Warehouse: "Lager", Qty: "Menge", "Valuation Rate": "Bewertungspreis", "Stock Value": "Lagerwert", Name: "Name", Status: "Status", Company: "Unternehmen", "From Date": "Von Datum", "To Date": "Bis Datum", "As of Date": "Stichtag", "Valid Till": "Gültig bis", "Net Total": "Nettobetrag", Tax: "Steuer", "Grand Total": "Gesamtbetrag", Frequency: "Frequenz", "Notes / Terms": "Notizen / Bedingungen", "Posting Date": "Buchungsdatum", "Transaction Date": "Belegdatum", "Delivery Date": "Lieferdatum", "% Delivered": "% geliefert", "% Billed": "% abgerechnet", "Update Stock": "Lager aktualisieren", "Payment Type": "Zahlungsart", "Paid From": "Bezahlt von", "Paid To": "Bezahlt an", "Paid Amount": "Zahlbetrag", Type: "Art", Total: "Gesamt", Allocated: "Zugeordnet", Unallocated: "Nicht zugeordnet", Remark: "Bemerkung", "Total Debit": "Summe Soll", "Total Credit": "Summe Haben", "Cost Center": "Kostenstelle", "Source Warehouse": "Quelllager", "Target Warehouse": "Ziellager", Source: "Quelle", Target: "Ziel", "Incoming Value": "Eingangswert", "Outgoing Value": "Ausgangswert", Rate: "Preis", "Income Account": "Ertragskonto", "Expense Account": "Aufwandskonto", Description: "Beschreibung", "Rate (%)": "Satz (%)", Mode: "Art", Change: "Rückgeld", Currency: "Währung", Title: "Titel", Selling: "Verkauf", Buying: "Einkauf", "Discount %": "Rabatt %", "Discount Amt": "Rabattbetrag", "Min Qty": "Mindestmenge", "Valid From": "Gültig ab", "Valid Upto": "Gültig bis", Priority: "Priorität", Enabled: "Aktiviert", "Fiscal Year": "Geschäftsjahr", "Budget Amount": "Budgetbetrag", "If Exceeded": "Bei Überschreitung", Month: "Monat", "Bank Account": "Bankkonto", Deposit: "Einzahlung", Withdrawal: "Auszahlung", "Reference No": "Referenznr.", "Matched Type": "Zugeordnete Art", "Matched Doc": "Zugeordneter Beleg", "Start Date": "Startdatum", "End Date": "Enddatum", "Billing Interval": "Abrechnungsintervall", "Current Period Start": "Aktueller Periodenbeginn", "Current Period End": "Aktuelles Periodenende", "Plan Items": "Planpositionen", "Total Amount": "Gesamtbetrag", "Stock Entry Type": "Lagerbuchungsart", "Rate Or Discount": "Preis oder Rabatt", "Discount Percentage": "Rabattprozentsatz", ID: "ID", "Customer Name": "Kundenname", "Customer Group": "Kundengruppe", Territory: "Region", "Credit Limit": "Kreditlimit", Email: "E-Mail", Phone: "Telefon", Address: "Adresse", City: "Stadt", "Zip Code": "PLZ", Country: "Land", "Tax ID": "Steuernummer", Website: "Webseite", "Contact Person": "Ansprechperson", "Contact Email": "E-Mail Ansprechperson", "Contact Phone": "Telefon Ansprechperson", "Supplier Name": "Lieferantenname", "Supplier Group": "Lieferantengruppe", "Item Group": "Artikelgruppe", "Stock UOM": "Lagereinheit", "Standard Rate": "Standardpreis", "Warehouse Name": "Lagername", "Company Name": "Firmenname", "Item (type)": "Artikel (Typ)", "Asset Tag / Plate": "Kennung / Kennzeichen", "Home Yard": "Heimdepot", "Purchase Value": "Kaufwert", "Meter Reading": "Zählerstand", "Meter Unit": "Zählereinheit", Yard: "Depot", "Asset (specific unit)": "Gerät (bestimmte Einheit)", Allocation: "Zuweisung", From: "Von", To: "Bis", Purpose: "Zweck", "Purchase Date": "Kaufdatum", Notes: "Notizen" }, vl = { items: "Positionen", taxes: "Steuern", references: "Zahlungsreferenzen", accounts: "Kontobuchungen", payments: "Zahlungen", plans: "Planpositionen", monthly_distribution: "Monatliche Verteilung" }, xl = { "Create Sales Order": "Kundenauftrag erstellen", "Create Sales Invoice": "Ausgangsrechnung erstellen", "Create Delivery Note": "Lieferschein erstellen", "Create Purchase Invoice": "Eingangsrechnung erstellen", "Create Purchase Receipt": "Wareneingang erstellen", "Create Credit Note": "Gutschrift erstellen", "Create Debit Note": "Lastschrift erstellen", "Create Return": "Retoure erstellen" }, kl = { totalRevenue: "Gesamtumsatz", outstandingReceivable: "Offene Forderungen", outstandingPayable: "Offene Verbindlichkeiten", totalStockValue: "Gesamter Lagerwert", recentDocuments: "Letzte Dokumente", noRecentDocuments: "Keine aktuellen Dokumente" }, Nl = { title: "Lambda ERP Chat", subtitle: "Bitten Sie mich, Dokumente zu erstellen, Daten abzurufen oder Berichte auszuführen.", s1: "Welche Kunden haben wir?", s2: "Zeig mir die Rohbilanz", s3: "Erstelle ein Angebot für 10 Bolt Pack M8", s4: "Liste alle unbezahlten Rechnungen auf", loadOlder: "Ältere Nachrichten laden", uploading: "Wird hochgeladen...", failed: "Fehlgeschlagen", remove: "Entfernen", placeholderDemo: "Live-Demo läuft...", placeholderType: "Nachricht eingeben...", placeholderConnecting: "Verbindung wird hergestellt...", attachTitle: "Datei anhängen (PDF oder Bild, max. 10 MB)", creating: "Chat wird erstellt...", dropToAttach: "Datei zum Anhängen ablegen (Bilder oder PDF, je max. 10 MB)", demoError: "Demo konnte nicht gestartet werden", voiceStart: "Sprachnachricht aufnehmen", voiceStop: "Aufnahme stoppen", voiceTranscribing: "Wird transkribiert...", micDenied: "Zugriff auf das Mikrofon wurde verweigert.", transcribeFailed: "Transkription fehlgeschlagen. Bitte erneut versuchen." }, wl = /* @__PURE__ */ JSON.parse('{"pageTitle":"Erste Schritte mit Lambda ERP","intro":"Lambda ERP ist ein KI-natives ERP-System, bei dem die gesamte Funktionalität – etwa das Erstellen von Aufträgen, das Prüfen von Beständen und das Erfassen von Zahlungen – über eine Chat-Oberfläche zugänglich ist. Es leitet an, denkt mit und prüft alle Ihre täglichen Geschäftsaufgaben.","openChat":"KI-Chat öffnen","tipLabel":"Tipp:","topTip":"Der KI-Chat kann Ihnen auch direkte Links zu Dokumenten und herunterladbaren PDFs geben. Bitten Sie ihn einfach, ein Dokument zu erstellen oder nachzuschlagen, und er fügt anklickbare Links in seine Antwort ein.","manualIntro":"Wenn Sie genau verstehen möchten, wie die Dinge unter der Haube funktionieren, oder Dinge lieber manuell erledigen, finden Sie hier einen vollständigen Geschäftszyklus Schritt für Schritt.","caTitle":"Dynamische Analysen – Chatten Sie sich zu jedem Diagramm","caBadge":"Kernfunktion","caBody":"Brauchen Sie einen Bericht, den die Vorlagen nicht liefern? Beschreiben Sie ihn einfach im Chat. Der Assistent ruft ein Code-Spezialmodell auf, das den Bericht für Sie schreibt, ihn live über Ihre ERP-Daten ausführt und einen teilbaren Link zurückgibt. Das Diagramm oder die Tabelle wird in Sekunden gerendert und als Entwurf unter „Eigene Analysen“ in der Seitenleiste gespeichert – Sie können ihn erneut öffnen, die URL mit Kollegen teilen oder den Assistenten bitten, ihn zu verfeinern.","caTryAsking":"Versuchen Sie zu fragen (zum Vorausfüllen im Chat klicken):","caPrompt1":"Zeig mir die Top 10 Kunden nach Umsatz dieses Jahr als Balkendiagramm","caPrompt2":"Monatlicher Einkaufstrend nach Lieferant über alle Monate, x-Achse mit Lieferantennamen beschriften","caPrompt3":"Bestseller-Artikel nach Menge, mit Tabelle und Diagramm","caPrompt4":"Welche Kunden schulden mir gerade am meisten?","caTryInChat":"Im Chat ausprobieren","caOpenWorkspace":"Analyse-Arbeitsbereich öffnen","flowTitle":"Überblick Dokumentenfluss","salesCycle":"Verkaufszyklus","purchaseCycle":"Einkaufszyklus","returns":"Retouren","flowShortcut":"Abkürzung: Das Angebot kann den Kundenauftrag überspringen und direkt zur Ausgangsrechnung oder zum Lieferschein führen","legendStock":"Lagerwirkung","legendGl":"Buchungswirkung","legendCash":"Geldwirkung","legendReversal":"Stornierung","lifecycleTitle":"Dokumenten-Lebenszyklus","lcDraftLabel":"Entwurf:","lcDraftDesc":"Bearbeitbar. Keine finanzielle oder Bestandswirkung.","lcSubmittedLabel":"Gebucht:","lcSubmittedDesc":"Gesperrt. Buchungen und Lagerbuchungen werden erzeugt.","lcCancelledLabel":"Storniert:","lcCancelledDesc":"Alle Buchungen storniert. Das Dokument wird dauerhaft archiviert.","lcNoDelete":"Es gibt kein Löschen. Um einen Entwurf zu verwerfen: buchen und dann stornieren.","lcCorrect":"Um ein gebuchtes Dokument zu korrigieren: stornieren und ein neues erstellen.","readyTitle":"Sie sind startklar","readyBody1":"Sie verstehen jetzt den vollständigen Zyklus. Ein typisches Praxisszenario sieht so aus: Sie erhalten einen Kundenauftrag (Angebot → Kundenauftrag), prüfen den Bestand, kaufen, was Sie brauchen (Bestellung → Wareneingang → Eingangsrechnung → Zahlung), liefern an den Kunden (Lieferschein), stellen ihm die Rechnung (Ausgangsrechnung) und ziehen die Zahlung ein (Zahlung). Kommt etwas zurück, erstellen Sie eine Gutschrift und eine Lieferschein-Retoure, um Finanzen und Bestand umzukehren. Die Rohbilanz bestätigt, dass alles ausgeglichen ist.","readyBody2":"Sie können all dies auch über den KI-Chat per natürlicher Konversation erledigen – beschreiben Sie einfach, was Sie brauchen, und er erstellt die richtigen Dokumente für Sie.","chips":{"Quotation":"Angebot","Sales Order":"Kundenauftrag","Delivery Note":"Lieferschein","Sales Invoice":"Ausgangsrechnung","Payment Entry":"Zahlung","Purchase Order":"Bestellung","Purchase Receipt":"Wareneingang","Purchase Invoice":"Eingangsrechnung","Credit Note":"Gutschrift","DN Return":"Lieferschein-Retoure","Debit Note":"Lastschrift","Draft":"Entwurf","Submitted":"Gebucht","Cancelled":"Storniert"},"links":{"Go to Setup":"Zur Einrichtung","Customers":"Kunden","Suppliers":"Lieferanten","Items":"Artikel","Warehouses":"Lager","New Quotation":"Neues Angebot","View Quotations":"Angebote anzeigen","Stock Balance":"Lagerbestand","New Purchase Order":"Neue Bestellung","Purchase Receipts":"Wareneingänge","Purchase Invoices":"Eingangsrechnungen","Delivery Notes":"Lieferscheine","Sales Invoices":"Ausgangsrechnungen","New Payment Entry":"Neue Zahlung","New Stock Entry":"Neue Lagerbuchung","New Journal Entry":"Neuer Buchungssatz","Profit & Loss":"Gewinn- und Verlustrechnung","Balance Sheet":"Bilanz","AR Aging":"Offene Posten Debitoren","AP Aging":"Offene Posten Kreditoren","Trial Balance":"Rohbilanz","General Ledger":"Hauptbuch","Open AI Chat":"KI-Chat öffnen"},"steps":{"1":{"title":"Unternehmen einrichten","description":"Erstellen Sie ein Unternehmen mit einer Basiswährung. Dadurch wird Ihr Kontenplan generiert (30 Standardkonten in den Bereichen Aktiva, Verbindlichkeiten, Eigenkapital, Erträge und Aufwendungen) sowie eine Standard-Kostenstelle. Sie können außerdem Demodaten mit Beispielkunden, -lieferanten und -artikeln anlegen, um schnell loszulegen."},"2":{"title":"Stammdaten anlegen","description":"Bevor Sie buchen können, benötigen Sie Stammdaten. Legen Sie mindestens einen Kunden, einen Lieferanten, einen Artikel (mit Standardpreis) und ein Lager an. Wenn Sie in Schritt 1 Demodaten angelegt haben, existieren diese bereits.","tip":"Artikel haben einen Standardpreis, der beim Hinzufügen zu Dokumenten automatisch ausgefüllt wird. Sie können den Preis pro Transaktion jederzeit überschreiben."},"3":{"title":"Angebot erstellen","description":"Ein Angebot ist eine unverbindliche Offerte an einen Kunden. Wählen Sie einen Kunden, fügen Sie Positionen mit Mengen und Preisen hinzu, optional Steuerzeilen, und speichern Sie. Angebote haben keine finanzielle Auswirkung – sie erzeugen keine Buchungs- oder Lagerbewegungen. Legen Sie ein Gültigkeitsdatum fest, damit das Angebot automatisch abläuft."},"4":{"title":"Buchen und in Kundenauftrag umwandeln","description":"Öffnen Sie Ihr gespeichertes Angebot und klicken Sie auf Buchen, um es zu bestätigen. Klicken Sie dann auf „Kundenauftrag erstellen“, um es umzuwandeln. Der Kundenauftrag stellt eine bestätigte Zusage des Kunden dar. Er hat weiterhin keine finanzielle Auswirkung, reserviert aber Bestand für die Planung.","tip":"Nur gebuchte Dokumente können in den nächsten Schritt umgewandelt werden. Entwurf → Buchen → Umwandeln ist der Standardablauf. Für schnelle Geschäfte können Sie den Kundenauftrag überspringen und direkt vom Angebot zur Ausgangsrechnung oder zum Lieferschein gehen."},"5":{"title":"Bestand vor der Erfüllung prüfen","description":"Bevor Sie liefern können, prüfen Sie, ob der Artikel tatsächlich auf Lager ist. Öffnen Sie den Bericht Lagerbestand und suchen Sie den Artikel. Wenn Ihr Lager eine Menge von null aufweist, müssen Sie zunächst Bestand beschaffen – entweder über eine Bestellung (Einkauf bei einem Lieferanten) oder eine Lagerbuchung (manueller Eingang).","tip":"Dies ist ein häufiges Szenario aus der Praxis: Sie verkaufen etwas und stellen dann fest, dass Sie es erst einkaufen müssen. Das ERP unterstützt beide Abläufe."},"6":{"title":"Einkaufszyklus: Bestand bei einem Lieferanten kaufen","description":"Wenn Sie Bestand einkaufen müssen, haben Sie zwei gültige Wege. Der Standardweg ist Bestellung → Wareneingang → Eingangsrechnung: Nutzen Sie ihn, wenn die Ware vor oder getrennt von der Lieferantenrechnung eintrifft. Treffen Rechnung und Wareneingang gleichzeitig ein, können Sie die Eingangsrechnung direkt erstellen und „Lager aktualisieren“ aktivieren, sodass dasselbe Dokument sowohl den Bestand vereinnahmt als auch die Verbindlichkeit erfasst.","tip":"Verwenden Sie zuerst den Wareneingang, wenn Sie Waren getrennt erhalten. Verwenden Sie die Eingangsrechnung mit „Lager aktualisieren“, wenn ein Schritt sowohl den Bestand vereinnahmen als auch die Lieferantenrechnung buchen soll. Setzen Sie in diesem direkten Weg ein Lager in jeder Bestandszeile."},"7":{"title":"Lieferschein erstellen","description":"Nachdem Sie nun Bestand haben, kehren Sie zu Ihrem gebuchten Kundenauftrag zurück und klicken Sie auf „Lieferschein erstellen“. Setzen Sie in jeder Position das Lager (von dem die Ware versendet wird) und buchen Sie. Dadurch wird Bestand aus dem Lager ausgebucht – Ihr Lagerbestand sinkt.","tip":"Der Lieferschein ist das Versanddokument. Er reduziert den Bestand, erzeugt aber keine Rechnung. Sie können Lieferung und Rechnungsstellung getrennt vornehmen."},"8":{"title":"Ausgangsrechnung erstellen und buchen","description":"Erstellen Sie aus dem gebuchten Kundenauftrag eine Ausgangsrechnung. Beim Buchen der Rechnung werden Buchungen erzeugt: Forderungen aus Lieferungen und Leistungen werden im Soll gebucht (der Kunde schuldet Ihnen Geld) und Umsatzerlöse im Haben (erzielter Ertrag). Sind Steuern konfiguriert, wird auch die Steuerverbindlichkeit im Haben gebucht. Der offene Betrag zeigt, was der Kunde noch schuldet."},"9":{"title":"Kundenzahlungen erfassen","description":"Erstellen Sie eine Zahlung, um vom Kunden erhaltenes Geld zu erfassen. Setzen Sie die Zahlungsart auf „Receive“ (Erhalten), wählen Sie den Kunden, geben Sie den Betrag an und ordnen Sie ihn der Ausgangsrechnung zu. Sie können Teilzahlungen vornehmen – der offene Betrag der Rechnung aktualisiert sich entsprechend. Erstellen Sie weitere Zahlungen, bis die Rechnung vollständig beglichen ist.","tip":"Teilzahlungen sind üblich. Eine Rechnung über 10.000 kann jetzt mit 3.000 und später mit 7.000 bezahlt werden. Jede Zahlung reduziert den offenen Betrag."},"10":{"title":"Lagerbuchungen (manuelle Bestandsführung)","description":"Verwenden Sie Lagerbuchungen für Bestandsbewegungen, die nicht mit Ein- oder Verkäufen verbunden sind. „Material Receipt“ fügt Bestand hinzu (Eröffnungsbestände, Korrekturen). „Material Issue“ bucht Bestand aus (Abschreibungen, interner Verbrauch). „Material Transfer“ verschiebt Bestand zwischen Lägern. Jede Buchung aktualisiert das Lagerbuch mit gleitender Durchschnittsbewertung.","tip":"Verwenden Sie für eingekaufte Waren Wareneingänge statt Lagerbuchungen – sie sind mit der Bestellung verknüpft und bieten einen sauberen Prüfpfad."},"11":{"title":"Buchungssätze","description":"Buchungssätze sind manuelle buchhalterische Anpassungen – Aufwandsabgrenzungen, Korrekturen, Umgliederungen, Eröffnungsbestände. Jeder Satz muss ausgeglichene Soll- und Haben-Beträge haben (Summe Soll = Summe Haben). Verwenden Sie sie, wenn kein anderer Dokumenttyp passt."},"12":{"title":"Gehaltszahlungen","description":"Lambda ERP wickelt Gehälter über die vorhandenen Buchhaltungswerkzeuge ab. Grenzen Sie zunächst den Gehaltsaufwand ab: Erstellen Sie einen Buchungssatz, der Gehaltsaufwand im Soll und Gehaltsverbindlichkeiten im Haben für den gesamten Lohnbetrag bucht. Bezahlen Sie dann die Mitarbeiter: Erstellen Sie einen weiteren Buchungssatz (oder eine Zahlung), der Gehaltsverbindlichkeiten im Soll und Ihr Bankkonto im Haben bucht. Dieser zweistufige Prozess hält Ihre Bücher korrekt – der Aufwand wird in der richtigen Periode erfasst und der Geldabfluss separat nachverfolgt.","tip":"Sie können den KI-Assistenten bitten, dies für Sie zu erledigen: „Grenze 15.000 an Gehältern für April ab“ gefolgt von „Zahle die April-Gehälter von der Bank“. Er erstellt die richtigen Buchungssätze automatisch."},"13":{"title":"Retouren und Gutschriften","description":"Wenn ein Kunde Waren zurückgibt oder Sie eine Gutschrift ausstellen müssen, erstellen Sie eine Retoure. Retouren verwenden denselben Dokumenttyp mit negativen Mengen. Öffnen Sie eine gebuchte Ausgangsrechnung und erstellen Sie eine Gutschrift (Ausgangsrechnungs-Retoure) – dies kehrt die Buchungen um und reduziert den offenen Betrag der ursprünglichen Rechnung. Erstellen Sie für den Bestand eine Lieferschein-Retoure, um Waren zurück ins Lager zu nehmen. Auf der Einkaufsseite erstellen Sie eine Lastschrift (Eingangsrechnungs-Retoure), um eine Lieferantenrechnung umzukehren, oder eine Wareneingangs-Retoure, um Waren zurückzusenden.","tip":"Eine Gutschrift ist einfach eine Ausgangsrechnung mit is_return=1 und negativen Mengen. Dieselbe Buchungslogik läuft – negative Beträge wechseln automatisch auf die richtige Soll-/Haben-Seite. Für eine vollständige Verkaufsretoure benötigen Sie sowohl eine Gutschrift (Finanzen) als auch eine Lieferschein-Retoure (Bestand)."},"14":{"title":"Berichte ausführen","description":"Prüfen Sie Ihre Bücher. Die Gewinn- und Verlustrechnung zeigt Erträge gegenüber Aufwendungen und den Periodengewinn. Die Bilanz zeigt Ihre finanzielle Lage (Aktiva = Verbindlichkeiten + Eigenkapital). Die Offenen Posten Debitoren zeigen, wer Ihnen Geld schuldet und wie überfällig es ist. Die Offenen Posten Kreditoren zeigen, was Sie Lieferanten schulden. Die Rohbilanz überprüft die doppelte Buchführung. Das Hauptbuch zeigt jede einzelne Buchung. Der Lagerbestand zeigt den aktuellen Bestand."},"15":{"title":"Arbeiten in Fremdwährungen","description":"Ihre Bücher werden in einer einzigen Basiswährung geführt (bei der Unternehmenseinrichtung gewählt), aber Sie können in jeder Währung buchen. Setzen Sie eine Währung auf einer Rechnung oder einem Beleg – oder geben Sie einem Kunden oder Lieferanten eine Standardwährung – und der Wechselkurs für dieses Datum wird automatisch ermittelt und auf dem Dokument gespeichert. Das Dokument behält seine Beträge in seiner eigenen Währung, während das Hauptbuch stets in Ihrer Basiswährung bucht. Wenn Sie später zu einem anderen Kurs einnehmen oder zahlen, wird der realisierte Währungsgewinn oder -verlust automatisch auf ein Konto Währungsgewinn/-verlust gebucht. Sie können sogar ein Fremdwährungs-Bankguthaben halten und es später zum Kurs Ihrer Bank umrechnen – die Differenz zum Buchwert wird dann realisiert. Zum Monatsende können Sie offene Fremdwährungssalden zum Stichtagskurs neu bewerten (ein unrealisierter Gewinn/Verlust, der in der nächsten Periode storniert wird), und Sie können jeden Jahresabschluss zur Anzeige in eine andere Währung umgerechnet betrachten.","tip":"Am einfachsten geht das über den KI-Chat – probieren Sie „Erstelle eine Ausgangsrechnung für Lumiere Audio in EUR“, „Zeig mir die Bilanz in EUR“ oder „Wie hoch ist unser unrealisiertes Währungsrisiko zum Monatsende?“. Die Demodaten enthalten bereits einen EUR-Kunden (Lumiere Audio SARL), dessen Rechnung zu einem anderen Kurs eingenommen wurde, sowie eine offene EUR-Lieferantenrechnung – öffnen Sie das Hauptbuch, um die realisierten Währungsbuchungen zu sehen."}},"setupTitle":"Richten Sie Ihr Unternehmen ein — KI-geführt","setupBadge":"Hier starten","setupBody":"Sagen Sie dem Assistenten, welche Art von Unternehmen Sie führen, und er erstellt Ihren Kontenplan für Sie — zugeschnitten auf Ihre Branche, mit den passenden Konten und sinnvollen Voreinstellungen. Er erklärt jede Entscheidung und legt erst dann etwas an, wenn Sie zustimmen.","setupSectors":"Zugeschnitten auf Dienstleistungen, Einzelhandel & POS, Gastronomie, Großhandel & Distribution, Import/Export, Fertigung und Bauwesen.","setupGetStarted":"Im Chat loslegen","setupManual":"Lieber ein manuelles Formular? Nutzen Sie die klassische Einrichtung.","setupManualLink":"Klassische Einrichtung","setupPrompt":"Ich möchte mein Unternehmen einrichten. Können Sie mich durch den Prozess führen?"}'), Cl = { title: "Formatierungshilfe", intro: "Der Text unter Notizen / Konditionen unterstützt eine einfache Formatierung im PDF:", heading: "fette Überschrift", emphasis: "kursiv / fett", rule: "horizontale Trennlinie", price: "rechtsbündiger Preis neben dem Text darüber (z. B. Monatlich | CHF 380.—)", block: "Trennen Sie Blöcke durch eine Leerzeile." }, Sl = { period: "Periode", allTime: "Gesamter Zeitraum", semantics: "Bilanzkonten: Saldo per Periodenende. Erfolgskonten: Bewegung innerhalb der Periode. Konto anklicken für den Kontoauszug.", empty: "Keine Konten gefunden. Zuerst die Firmeneinrichtung abschliessen.", disabled: "deaktiviert", rootType: { Asset: "Aktiven", Liability: "Passiven", Equity: "Eigenkapital", Income: "Ertrag", Expense: "Aufwand" } }, Al = { fleetCalendar: "Flottenkalender", yard: "Depot", machineType: "Maschinentyp", prev: "Zurück", today: "Heute", next: "Weiter", weeks: "Wochen", newBooking: "Neue Reservation", reserved: "Reserviert", onHire: "Vermietet", machine: "Maschine", loadError: "Flottenkalender konnte nicht geladen werden.", noAssets: "Keine Geräte vorhanden. Maschinen unter Vermietung → Flotte hinzufügen.", pooledNote: "{{count}} Pool-Reservation(en) noch ohne Maschinenzuordnung", unassigned: "Pool — noch nicht zugeordnet", poolWarning: "Die Pool-Kapazität ist reserviert. Eine konkrete Maschine ist noch nicht zugeordnet; vor der Ausgabe eine Maschine auswählen." }, _l = { Unit: "Bestimmte Maschine", Pool: "Pool – Maschine später zuweisen", Available: "Verfügbar", "On Hire": "Vermietet", Out: "Ausgeliefert", Maintenance: "Wartung", Retired: "Ausgemustert", Reserved: "Reserviert", Returned: "Zurückgegeben", Cancelled: "Storniert", Customer: "Kunde", Supplier: "Lieferant" }, Dl = { asset: { item_code: "Der Artikel ist der Maschinen-TYP und trägt den Tarif; ein Gerät ist eine physische Einheit davon. Der Artikel muss is_asset_tracked = 1 haben.", asset_tag: "Kennzeichen oder Seriennummer — muss über alle Geräte eindeutig sein.", status: "Aktueller Zustand der Einheit (kein Zeitraum). Ausgemusterte Einheiten verlassen den Pool.", meter_reading: "Betriebsstunden / km." }, reservation: { item_code: "Pool-Reservation ('ein 1.7t-Bagger'): Artikel + Depot + Menge setzen. Für eine bestimmte Maschine stattdessen Gerät setzen.", asset: "Eine bestimmte Maschine buchen. Eine Einheiten-Buchung belegt auch einen Platz im Artikel+Depot-Pool.", from_datetime: "Genauer Beginn — Datum und Uhrzeit (z. B. 14:27). Ein reines Datum bedeutet 00:00.", to_datetime: "Genaues Ende. Fenster sind halboffen [von, bis): eine Miete, die um 09:00 endet, und die nächste um 09:00 beginnende, kollidieren NICHT." } }, Pl = { label: "Standard-Verkaufssteuer", none: "Keine automatische Übernahme", hint: "Wird bei neuen Offerten, Aufträgen und Rechnungen übernommen, sofern keine Steuern ausdrücklich vorgegeben werden. Bestehende Belege bleiben unverändert. Mit „Speichern“ übernehmen.", saveFirst: "Firma zuerst speichern. Anschliessend können Sie eine ihrer Verkaufsteuervorlagen auswählen.", loadError: "Steuervorlagen konnten nicht geladen werden. Die gespeicherte Auswahl bleibt erhalten.", retry: "Erneut versuchen", unavailable: "Die gespeicherte Vorlage ist für diese Firma nicht als Verkaufsteuervorlage verfügbar. Bitte prüfen Sie die Auswahl.", empty: "Für diese Firma sind noch keine Verkaufsteuervorlagen hinterlegt." }, Tl = { title: "App verbinden", subtitle: "Wählen Sie, wo Sie Lambda ERP nutzen möchten. Wir zeigen Ihnen die passende Einrichtung.", other: "Etwas anderes", customName: "Eigene Integration", signIn: "Anmelden", apiKey: "API-Schlüssel", access: "Zugriff", role_viewer: "Daten ansehen", role_manager: "Erstellen und bearbeiten", role_admin: "Voller administrativer Zugriff", roleHelp: "Der Zugriff ist immer durch Ihre eigenen ERP-Berechtigungen begrenzt. Wählen Sie nur, was diese App benötigt.", copied: "Kopiert", copyFailed: "Automatisches Kopieren fehlgeschlagen. Markieren und kopieren Sie den Text manuell.", retry: "Erneut versuchen", interface: "Verbindungsart", chatDisabled: "Der Chat-API-Zugriff ist deaktiviert. Ein Administrator kann ihn oben in den API-Einstellungen aktivieren.", restDisabled: "Der REST-API-/MCP-Zugriff ist deaktiviert. Ein Administrator muss ihn in den API-Einstellungen aktivieren, bevor sich diese App verbinden kann.", chatgptStep: "Öffnen Sie chatgpt.com im Browser. Wählen Sie «Plugins» in der linken Seitenleiste, klicken Sie oben rechts auf «Add» (+) und wählen Sie «Create MCP App». Geben Sie einen Namen, etwa Lambda ERP, und eine kurze Beschreibung ein.", chatgptDeveloperMode: "Falls «Create MCP App» nicht verfügbar ist, prüfen Sie unter «Settings» → «Security and login», ob «Developer mode» aktiviert ist. Ist er bereits aktiviert, gehen Sie direkt zu «Plugins». Zusätzlich können Workspace-Berechtigungen erforderlich sein.", claudeStep: "Öffnen Sie in Claude Desktop «Settings» → «Connectors» und wählen Sie «Add custom connector».", urlStep: "Fügen Sie diese Server-URL ein. Verwenden Sie die OAuth-Anmeldung; lassen Sie optionale Felder für Client-ID und Client-Secret leer.", approveStep: "Verbinden Sie die App, melden Sie sich bei Lambda ERP an und bestätigen Sie den gewünschten Zugriff.", noKeyNeeded: "Sie benötigen weder API-Schlüssel noch eigene Header. Den Zugriff wählen Sie auf der Bestätigungsseite.", officialGuide: "Einrichtungsanleitung der App öffnen", copyUrl: "Server-URL kopieren", keyReady: "Ihr Verbindungsschlüssel ist bereit", copySetup: "Einrichtung kopieren", copyHeader: "Autorisierungsheader kopieren", cliStep: "Führen Sie diesen Befehl im Terminal aus. Prüfen Sie die Verbindung anschliessend mit /mcp in Claude Code.", codexStep: "Fügen Sie diesen Eintrag Ihrer Codex-Konfiguration hinzu. Starten Sie Codex neu und prüfen Sie /mcp.", done: "Fertig — Schlüssel ausblenden", existing: "Bestehende Verbindungen", refresh: "Aktualisieren", lastUsed: "Zuletzt verwendet", notUsed: "Noch nicht verwendet", consentTitle: "Mit Lambda ERP verbinden", expired: "Diese Verbindungsanfrage fehlt oder ist abgelaufen. Starten Sie erneut in Ihrer App.", loginFirst: "Melden Sie sich bei Ihrem ERP-Konto an, um die Anfrage dieser App zu prüfen.", consentBody: "«{{app}}» möchte auf Ihr ERP zugreifen. Wählen Sie den Zugriff, den Sie erlauben möchten.", returnTo: "Nach der Bestätigung kehren Sie hierhin zurück", revokeHelp: "Sie können diese Verbindung jederzeit unter Einstellungen → API-Zugriff widerrufen.", deny: "Abbrechen", allow: "Verbindung erlauben", explain_viewer: "Die App kann ERP-Datensätze und Berichte lesen sowie Beleg-PDFs erstellen. Sie kann keine Geschäftsdaten bearbeiten.", explain_manager: "Die App kann zusätzlich Belege erstellen, bearbeiten, buchen und stornieren sowie Stammdaten aktualisieren.", explain_admin: "Die App kann alle verfügbaren ERP-Werkzeuge nutzen, einschliesslich administrativer Aktionen und dem Löschen von Stammdaten." }, Rl = {
8147
8147
  proposal: tl,
8148
8148
  common: nl,
8149
8149
  datePresets: al,
@@ -8174,7 +8174,7 @@ const Co = { incomplete: "Draft saved; PDF output requires Customer, Company and
8174
8174
  hints: Dl,
8175
8175
  companyTax: Pl,
8176
8176
  connections: Tl
8177
- }, Bl = { incomplete: "Brouillon enregistré. Le PDF nécessite un client, une entreprise et au moins une offre." }, Il = { save: "Enregistrer", saving: "Enregistrement...", submit: "Valider", cancel: "Annuler", cancelDoc: "Annuler", cancelDocAbort: "Ne pas annuler", cancelDocConfirmBtn: "Annuler définitivement", cancelBooking: "Annuler la réservation", cancelBookingConfirm: "Oui, annuler", keepBooking: "Conserver", discardDraft: "Supprimer le brouillon", keepDraft: "Conserver", discardDraftConfirm: "Supprimer", showDiscarded: "Afficher les supprimés", showDisabled: "Afficher les désactivés", columns: "Colonnes", searchPlaceholder: "Rechercher…", noRecords: "Aucun enregistrement trouvé", active: "Actif", disabled: "Désactivé", sortByColumn: "Trier par cette colonne", delete: "Supprimer", edit: "Modifier", pdf: "PDF", addRow: "Ajouter une ligne", loading: "Chargement...", enable: "Activer", disable: "Désactiver", refresh: "Actualiser", refreshing: "Actualisation", areYouSure: "Êtes-vous sûr ?", all: "Tous", apply: "Appliquer", new: "Nouveau", prev: "Précédent", next: "Suivant", perPage: "Par page", page: "Page", of: "sur", showing: "Affichage", total: "Total", totals: "Totaux", errorOccurred: "Une erreur s'est produite" }, El = { today: "Aujourd’hui", thisWeek: "Cette semaine", thisMonth: "Ce mois-ci", thisQuarter: "Ce trimestre", thisYear: "Cette année", lastMonth: "Mois dernier", lastQuarter: "Trimestre dernier", lastYear: "Année dernière", endOfThisMonth: "Fin du mois en cours", endOfLastMonth: "Fin du mois dernier", endOfThisQuarter: "Fin du trimestre en cours", endOfThisYear: "Fin de l’année en cours", endOfLastYear: "Fin de l’année dernière" }, Ll = { title: "Importer un relevé bancaire", subtitle: "Prévisualisez et importez des relevés ISO 20022 CAMT sans écriture comptable automatique.", selectFiles: "Fichiers CAMT", chooseFiles: "Choisir des fichiers XML ou ZIP", dropFiles: "Déposez les fichiers XML ou ZIP ici", unsupportedFiles: "Seuls les fichiers CAMT XML ou ZIP sont acceptés : {{files}}", fileHint: "camt.053.001.04 et camt.053.001.08 · plusieurs fichiers acceptés", preview: "Afficher l’aperçu", noPosting: "L’aperçu et l’importation ne génèrent aucune écriture comptable.", alreadyImported: "Déjà importé", period: "Période", openingBalance: "Solde initial", closingBalance: "Solde final", entries: "Opérations", credits: "Crédits", debits: "Débits", details: "Détails des transactions", batches: "lots", qrReferences: "Références QR", mapping: "Affectation du compte bancaire", selectBankAccount: "Sélectionner un compte bancaire…", noBankAccounts: "Aucun compte bancaire n’a encore été configuré.", createBankAccount: "Configurer un compte bancaire", mappingRequired: "Affectez chaque nouveau relevé à un compte bancaire avant l’importation.", import: "Importer les relevés", importComplete: "Importation terminée", importCounts: "{{imported}} opérations importées, {{duplicates}} doublons ignorés", history: "Importations récentes", importId: "Importation", bankAccount: "Compte bancaire", source: "Fichier source", openReconciliation: "Ouvrir le rapprochement bancaire" }, Ol = { title: "Rapprochement bancaire", subtitle: "Rapprochez les transactions importées avec des écritures, des factures ouvertes ou des comptes comptables.", status: "Statut", unreconciled: "Non rapprochée", reconciled: "Rapprochée", transactions: "Transactions bancaires", empty: "Aucune transaction bancaire dans cette vue.", remittance: "Information de paiement", reference: "Référence", openTransaction: "Ouvrir la transaction bancaire", conversionRate: "Taux de change (1 {{currency}} en {{base}})", conversionRateHint: "Requis si aucun taux de change n’existe à la date de la transaction.", existingVouchers: "Écritures existantes", existingVouchersHint: "Ces pièces validées contiennent un mouvement exact sur ce compte bancaire. Le rapprochement ne les comptabilise pas une seconde fois.", match: "Rapprocher", openInvoices: "Factures ouvertes", openInvoicesHint: "Sélectionnez une ou plusieurs factures du même tiers. La confirmation crée et valide un paiement.", outstanding: "Solde ouvert", allocation: "Affectation", noInvoiceSuggestions: "Aucune facture ouverte plausible n’a été trouvée.", allocated: "{{amount}} affectés sur {{total}}", createPayment: "Créer le paiement et rapprocher", directPosting: "Comptabiliser sur un compte", directPostingHint: "Pour les frais, intérêts et autres mouvements sans facture. Les comptes clients, fournisseurs, bancaires et de caisse sont volontairement exclus.", counterpartyAccount: "Compte de contrepartie", remarks: "Remarque", createJournal: "Créer l’écriture et rapprocher", activeMatch: "Rapprochement actif", undo: "Annuler le rapprochement", confirmExistingTitle: "Rapprocher l’écriture existante ?", confirmExistingBody: "Rapprocher {{transaction}} avec {{voucher}} ? Aucune nouvelle écriture ne sera comptabilisée.", confirmGroupBody: "Rapprocher les {{count}} transactions, pour un total de {{amount}}, avec {{voucher}} ? Aucune nouvelle écriture ne sera comptabilisée.", groupedTransactions: "{{count}} transactions groupées", confirmPaymentTitle: "Comptabiliser le paiement et rapprocher ?", confirmPaymentBody: "Cette action valide un paiement de {{amount}} pour {{transaction}} et l’affecte à {{count}} facture(s).", onAccountWarning: "{{amount}} resteront comme paiement non affecté pour ce tiers.", confirmJournalTitle: "Comptabiliser l’écriture et rapprocher ?", confirmJournalBody: "Cette action comptabilise {{amount}} pour {{transaction}} sur {{account}}.", postAndReconcile: "Comptabiliser et rapprocher", undoTitle: "Annuler le rapprochement ?", undoCreatedBody: "La pièce créée par ce rapprochement sera annulée, ses écritures extournées et les affectations aux factures restaurées.", undoExistingBody: "La pièce existante sera uniquement dissociée. Elle restera validée et ses écritures ne seront pas modifiées.", undoExistingGroupBody: "Les {{count}} transactions groupées ({{transactions}}) seront toutes dissociées ensemble. La pièce existante restera validée et ses écritures ne seront pas modifiées.", reconciledNotice: "Transaction bancaire rapprochée.", groupReconciledNotice: "{{count}} transactions bancaires ont été rapprochées en groupe.", undoneNotice: "Rapprochement annulé." }, Fl = { placeholder: "Rechercher ou saisir champ:valeur…", search: "Rechercher", addFilter: "+ Filtre", availableFields: "Champs disponibles", fieldHelp: "Choisissez un champ ou saisissez son nom technique suivi de deux-points.", field: "Champ", valueFor: "Valeur pour {{label}}", allFields: "Tous les champs", typeForValues: "Commencez à saisir pour rechercher des valeurs dans ce champ.", noValues: "Aucune valeur correspondante.", noFields: "Aucun champ correspondant.", useValue: "Utiliser « {{value}} »", removeFilter: "Supprimer le filtre {{label}}", types: { text: "Texte", textarea: "Texte long", select: "Sélection", link: "Enregistrement lié", date: "Date", datetime: "Date et heure", number: "Nombre", currency: "Montant" } }, zl = { title: "Langue", help: "Choisissez la langue de l'interface. Votre choix est enregistré dans ce navigateur.", label: "Langue" }, ql = { back: "Retour", logout: "Déconnexion", openMenu: "Ouvrir le menu", closeMenu: "Fermer le menu", new: "Nouveau : {{label}}" }, Ml = { dashboard: "Tableau de bord", setup: "Configuration de l'entreprise", tutorial: "Premiers pas", chat: "Chat", reports: "Rapports", "trial-balance": "Balance comptable", "general-ledger": "Grand livre", "stock-balance": "Solde de stock", "profit-and-loss": "Compte de résultat", "balance-sheet": "Bilan", "ar-aging": "Balance âgée clients", "ap-aging": "Balance âgée fournisseurs", analytics: "Analyses", "chart-of-accounts": "Plan comptable" }, $l = { quotation: { one: "Devis", other: "Devis" }, proposal: { one: "Offre groupée", other: "Offres groupées" }, "sales-order": { one: "Commande client", other: "Commandes client" }, "sales-invoice": { one: "Facture de vente", other: "Factures de vente" }, "pos-invoice": { one: "Ticket de caisse", other: "Tickets de caisse" }, "purchase-order": { one: "Bon de commande", other: "Bons de commande" }, "purchase-invoice": { one: "Facture d'achat", other: "Factures d'achat" }, "payment-entry": { one: "Paiement", other: "Paiements" }, "journal-entry": { one: "Écriture comptable", other: "Écritures comptables" }, "bank-transaction": { one: "Transaction bancaire", other: "Transactions bancaires" }, "bank-account": { one: "Compte bancaire", other: "Comptes bancaires" }, budget: { one: "Budget", other: "Budgets" }, subscription: { one: "Abonnement", other: "Abonnements" }, "stock-entry": { one: "Mouvement de stock", other: "Mouvements de stock" }, "delivery-note": { one: "Bon de livraison", other: "Bons de livraison" }, "purchase-receipt": { one: "Bon de réception", other: "Bons de réception" }, "pricing-rule": { one: "Règle de prix", other: "Règles de prix" } }, Vl = { company: { one: "Société", other: "Sociétés" }, customer: { one: "Client", other: "Clients" }, supplier: { one: "Fournisseur", other: "Fournisseurs" }, item: { one: "Article", other: "Articles" }, warehouse: { one: "Entrepôt", other: "Entrepôts" } }, Ul = { chats: "Discussions", newChat: "Nouvelle discussion", deleteChat: "Supprimer la discussion", customAnalytics: "Analyses personnalisées", newAnalytics: "Nouvelle analyse", deleteReport: "Supprimer le rapport", groups: { Introduction: "Introduction", Selling: "Ventes", Buying: "Achats", Accounting: "Comptabilité", Stock: "Stock", Reports: "Rapports", Masters: "Données de base", Settings: "Paramètres", Rentals: "Locations" }, items: { "Getting Started": "Premiers pas", "Company Setup": "Configuration de l'entreprise", "Opening Balances": "Soldes d'ouverture", Quotation: "Devis", Proposal: "Offre groupée", "Sales Order": "Commande client", "Sales Invoice": "Facture de vente", "POS Invoice": "Ticket de caisse", "Purchase Order": "Bon de commande", "Purchase Invoice": "Facture d'achat", "Payment Entry": "Paiement", "Journal Entry": "Écriture comptable", "Bank Transaction": "Transaction bancaire", "Bank Account": "Comptes bancaires", "Bank Statement Import": "Importer un relevé bancaire", "Bank Reconciliation": "Rapprochement bancaire", Budget: "Budget", Subscription: "Abonnement", "Stock Entry": "Mouvement de stock", "Delivery Note": "Bon de livraison", "Purchase Receipt": "Bon de réception", "Trial Balance": "Balance comptable", "Profit & Loss": "Compte de résultat", "Balance Sheet": "Bilan", "General Ledger": "Grand livre", "AR Aging": "Balance âgée clients", "AP Aging": "Balance âgée fournisseurs", Analytics: "Analyses", "Stock Balance": "Solde de stock", Company: "Société", Customer: "Client", Supplier: "Fournisseur", Item: "Article", Warehouse: "Entrepôt", General: "Général", "Pricing Rule": "Règle de prix", "Users & Team": "Utilisateurs et équipe", "Chart of Accounts": "Plan comptable", "Fleet Calendar": "Calendrier de flotte", "Fleet (Assets)": "Flotte (matériel)", Reservations: "Réservations" } }, Kl = { taglineFirstRun: "Créez votre compte administrateur pour commencer", taglineRegister: "Créez votre compte", taglineSignIn: "Connectez-vous à votre compte", demoBanner: "Le mode démo est actif. Connectez-vous avec un compte administrateur pour gérer les paramètres ou désactiver l'accès public.", fullName: "Nom complet", email: "E-mail", password: "Mot de passe", confirmPassword: "Confirmer le mot de passe", createAdmin: "Créer un compte administrateur", register: "S'inscrire", signIn: "Se connecter", browsingTitle: "Simple visite ?", browsingBody: "Démarrez une nouvelle session de chat et regardez la démo en direct.", enterDemo: "Lancer la démo", haveInvite: "Vous avez une invitation ?", noAccountPrompt: "Besoin d'un compte ?", registerLink: "S'inscrire", alreadyHaveAccount: "Vous avez déjà un compte ?", signInLink: "Se connecter", passwordsNoMatch: "Les mots de passe ne correspondent pas", passwordTooShort: "Le mot de passe doit comporter au moins 6 caractères", registrationFailed: "Échec de l'inscription", loginFailed: "Échec de la connexion", orContinueWith: "ou continuer avec", continueWithProvider: "Continuer avec {{provider}}", oauthError: { generic: "Échec de la connexion. Veuillez réessayer.", email_exists: "Un compte avec cet e-mail existe déjà. Connectez-vous, puis liez ce fournisseur depuis les paramètres du compte.", email_unverified: "Votre fournisseur n'a pas confirmé d'e-mail vérifié, la connexion est donc impossible.", registration_closed: "Cet e-mail n'a pas encore de compte et l'inscription se fait uniquement sur invitation.", invite_invalid: "Cette invitation est invalide, déjà utilisée ou émise pour un autre e-mail.", identity_taken: "Ce compte fournisseur est déjà lié à un autre utilisateur.", account_disabled: "Ce compte a été désactivé.", token_exchange_failed: "Échec de la connexion auprès du fournisseur. Veuillez réessayer." } }, jl = { pdfTitle: "PDF et impression", pageSize: "Format de page", pdfHelp: "A4 est la norme internationale (210 x 297 mm). Letter est la norme aux États-Unis (8,5 x 11 po).", passwordTitle: "Changer le mot de passe", currentPassword: "Mot de passe actuel", newPassword: "Nouveau mot de passe", confirmNewPassword: "Confirmer le nouveau mot de passe", changePassword: "Changer le mot de passe", changingPassword: "Modification...", passwordChanged: "Mot de passe modifié.", setPasswordTitle: "Définir un mot de passe", setPasswordBody: "Votre compte se connecte avec Google. Définissez un mot de passe pour vous connecter aussi avec e-mail et mot de passe — un recours pratique si vous perdez l'accès à votre compte Google.", setPassword: "Définir le mot de passe", passwordSet: "Mot de passe défini.", linkedAccountsTitle: "Comptes liés", linkedAccountsBody: "Liez un compte Google ou Apple pour vous connecter sans mot de passe.", linked: "Lié", linkProvider: "Lier {{provider}}", openingTitle: "Soldes d'ouverture", openingBody: "La page Soldes d'ouverture permet d'importer les soldes des comptes, le stock et les factures impayées d'un système précédent.", openingEnabled: "Actuellement activé — accessible sous Introduction > Soldes d'ouverture.", openingDisabled: "Actuellement désactivé — la page est masquée dans la barre latérale.", signupTitle: "Inscription publique", signupBody: "Permettre à quiconque de créer son propre compte (en tant que lecteur) sans invitation. Lorsque désactivé, les nouveaux utilisateurs ne peuvent rejoindre que via un lien d'invitation.", signupEnabled: "Actuellement activé — toute personne disposant de l'URL peut s'inscrire en tant que lecteur.", signupDisabled: "Actuellement désactivé — l'inscription se fait uniquement sur invitation.", chatApiTitle: "API de chat", apiTitle: "API programmatique", restApiBody: "Autoriser les applications et scripts à accéder à cet ERP via REST ou MCP, avec des clés API ou une connexion OAuth. Désactivé par défaut.", restApiEnabled: "Actuellement activé — les clés émises peuvent appeler /api/documents, /api/masters et le reste de l'API REST.", restApiDisabled: "Actuellement désactivé — l'API REST n'accepte que le cookie de session ; les clés sont rejetées.", restApiDeactivateTitle: "Désactiver l'API REST ?", restApiDeactivateBody: "L'accès par clé Bearer à l'API REST sera désactivé — les connecteurs et scripts sont rejetés jusqu'à sa réactivation. L'application web (cookie de session) n'est pas affectée, et vos clés API ne sont PAS supprimées : elles fonctionnent à nouveau dès que vous réactivez l'API.", chatApiBody: "Permettre à des applications externes de dialoguer avec l'agent de chat de cet ERP via une API HTTP, à l'aide de clés API Bearer. Désactivé par défaut.", chatApiEnabled: "Actuellement activé — les clés émises peuvent appeler POST /api/v1/chat.", chatApiDisabled: "Actuellement désactivé — l'API renvoie 404 et les clés ne fonctionnent pas.", chatApiDeactivateTitle: "Désactiver l'API chat ?", chatApiDeactivateBody: "L'API chat sera désactivée — toute requête programmatique (clés API Bearer) est rejetée jusqu'à sa réactivation. Vos clés API ne sont PAS supprimées : elles restent enregistrées et fonctionnent à nouveau dès que vous réactivez l'API.", chatApiDeactivateConfirm: "Désactiver l'API", chatApiKeysTitle: "Applications et clés API", chatApiTokenOnce: "Copiez ce jeton maintenant — il ne sera plus affiché.", chatApiCopy: "Copier", chatApiDismiss: "Fermer", chatApiRevoked: "révoquée", chatApiRevoke: "Révoquer", chatApiNoKeys: "Aucune connexion pour le moment.", chatApiName: "Nom", chatApiRole: "Rôle", chatApiCreate: "Créer une clé", chatApiWarn: "Une clé agit avec votre identité et au plus votre rôle — à traiter comme un mot de passe. Révoquer d'abord, puis supprimer.", publicTitle: "Accès public (mode démo)", publicActive: "Actif", publicActiveBody: "L'accès public est activé. N'importe qui peut utiliser l'application sans se connecter. Tous les visiteurs obtiennent des autorisations de niveau gestionnaire.", disablePublic: "Désactiver l'accès public", disabling: "Désactivation...", publicInactiveBody: "Activez l'accès public pour permettre à quiconque d'utiliser l'application sans compte. Utile pour les démos et présentations. Tous les visiteurs obtiennent des autorisations de niveau gestionnaire et partagent la même identité.", enablePublic: "Activer l'accès public", enableWarning: "Cela permettra à quiconque d'accéder à l'application sans se connecter. Ils pourront créer, modifier et valider des documents. Votre compte administrateur nécessite toujours une connexion.", yesEnable: "Oui, activer", enabling: "Activation...", tokenSpendTitle: "Dépenses de jetons", chatApiDelete: "Supprimer", chatApiYourKeys: "Vos connexions", chatApiYouBadge: "vous", chatApiUnknownUser: "Utilisateur inconnu", chatApiAdminAllNote: "En tant qu’administrateur, vous pouvez gérer les connexions de tous les utilisateurs. Les vôtres apparaissent en premier.", chatApiConfirmTitle: "Cette connexion appartient à un autre utilisateur", chatApiConfirmRevokeBody: "« {{key}} » appartient à {{name}}. La révocation déconnectera immédiatement son application. Continuer ?", chatApiConfirmDeleteBody: "« {{key}} » appartient à {{name}}. Supprimer définitivement cette connexion révoquée ?", chatApiPersonal: "Connectez des applications à votre compte ERP. L’accès ne dépasse jamais vos droits actuels.", version: "Version {{version}}" }, Wl = { Draft: "Brouillon", Open: "Ouvert", Submitted: "Validé", "To Deliver and Bill": "À livrer et facturer", "To Deliver": "À livrer", "To Bill": "À facturer", Completed: "Terminé", Ordered: "Commandé", Paid: "Payé", Unpaid: "Impayé", Cancelled: "Annulé", Discarded: "Supprimé", Overdue: "En retard", Return: "Retour", Closed: "Clôturé" }, Gl = { periodRange: "Période du rapport : {{from}} – {{to}}", periodFrom: "Période du rapport à partir du {{from}}", periodThrough: "Période du rapport jusqu’au {{to}}", asOfDate: "Date de référence : {{date}}", balanced: "ÉQUILIBRÉ", imbalanced: "DÉSÉQUILIBRÉ", income: "Produits", expenses: "Charges", totalIncome: "Total des produits", totalExpenses: "Total des charges", netProfit: "Bénéfice", netLoss: "Perte", assets: "Actif", liabilities: "Passif", equity: "Capitaux propres", totalAssets: "Total de l'actif", totalLiabilities: "Total du passif", totalEquity: "Total des capitaux propres", totalLiabilitiesEquity: "Total passif + capitaux propres", current: "Non échu", noData: "Aucune donnée trouvée", noEntries: "Aucune écriture trouvée", noStockData: "Aucune donnée de stock trouvée", noReceivables: "Aucune créance en cours", noPayables: "Aucune dette en cours", noDocuments: "Aucun document trouvé" }, Hl = { required: "Requis : {{fields}}", deleteConfirm: "Supprimer ce {{label}} ?", disabledNotice: "Ce {{label}} est désactivé. Il reste dans le système car d'autres enregistrements y font référence.", disabledOnDelete: "{{label}} {{name}} a été désactivé au lieu d'être supprimé car il est référencé par {{reason}}." }, Ql = { "Last modified": "Dernière modification", Created: "Créé", Account: "Compte", Amount: "Montant", Balance: "Solde", Credit: "Crédit", Debit: "Débit", Date: "Date", Party: "Tiers", "Party Type": "Type de tiers", "Voucher Type": "Type de pièce", "Voucher No": "N° de pièce", Invoice: "Facture", Customer: "Client", Supplier: "Fournisseur", "Due Date": "Date d'échéance", Outstanding: "Encours", "Outstanding Amount": "Montant dû", "Item Code": "Code article", "Item Name": "Nom de l'article", Item: "Article", Warehouse: "Entrepôt", Qty: "Qté", "Valuation Rate": "Coût unitaire", "Stock Value": "Valeur du stock", Name: "Nom", Status: "Statut", Company: "Société", "From Date": "Date de début", "To Date": "Date de fin", "As of Date": "À la date", "Valid Till": "Valable jusqu'au", "Net Total": "Total net", Tax: "Taxe", "Grand Total": "Total TTC", Frequency: "Fréquence", "Notes / Terms": "Notes / Conditions", "Posting Date": "Date de comptabilisation", "Transaction Date": "Date de transaction", "Delivery Date": "Date de livraison", "% Delivered": "% livré", "% Billed": "% facturé", "Update Stock": "Mettre à jour le stock", "Payment Type": "Type de paiement", "Paid From": "Payé depuis", "Paid To": "Payé à", "Paid Amount": "Montant payé", Type: "Type", Total: "Total", Allocated: "Affecté", Unallocated: "Non affecté", Remark: "Remarque", "Total Debit": "Total débit", "Total Credit": "Total crédit", "Cost Center": "Centre de coûts", "Source Warehouse": "Entrepôt source", "Target Warehouse": "Entrepôt cible", Source: "Source", Target: "Cible", "Incoming Value": "Valeur entrante", "Outgoing Value": "Valeur sortante", Rate: "Prix unitaire", "Income Account": "Compte de produits", "Expense Account": "Compte de charges", Description: "Description", "Rate (%)": "Taux (%)", Mode: "Mode", Change: "Monnaie rendue", Currency: "Devise", Title: "Titre", Selling: "Vente", Buying: "Achat", "Discount %": "Remise %", "Discount Amt": "Montant remise", "Min Qty": "Qté min", "Valid From": "Valable à partir du", "Valid Upto": "Valable jusqu'au", Priority: "Priorité", Enabled: "Activé", "Fiscal Year": "Exercice", "Budget Amount": "Montant du budget", "If Exceeded": "Si dépassé", Month: "Mois", "Bank Account": "Compte bancaire", Deposit: "Dépôt", Withdrawal: "Retrait", "Reference No": "N° de référence", "Matched Type": "Type rapproché", "Matched Doc": "Pièce rapprochée", "Start Date": "Date de début", "End Date": "Date de fin", "Billing Interval": "Intervalle de facturation", "Current Period Start": "Début période actuelle", "Current Period End": "Fin période actuelle", "Plan Items": "Articles du plan", "Total Amount": "Montant total", "Stock Entry Type": "Type de mouvement de stock", "Rate Or Discount": "Prix ou remise", "Discount Percentage": "Pourcentage de remise", ID: "ID", "Customer Name": "Nom du client", "Customer Group": "Groupe de clients", Territory: "Territoire", "Credit Limit": "Limite de crédit", Email: "E-mail", Phone: "Téléphone", Address: "Adresse", City: "Ville", "Zip Code": "Code postal", Country: "Pays", "Tax ID": "N° de TVA", Website: "Site web", "Contact Person": "Personne de contact", "Contact Email": "E-mail du contact", "Contact Phone": "Téléphone du contact", "Supplier Name": "Nom du fournisseur", "Supplier Group": "Groupe de fournisseurs", "Item Group": "Groupe d'articles", "Stock UOM": "Unité de stock", "Standard Rate": "Prix standard", "Warehouse Name": "Nom de l'entrepôt", "Company Name": "Nom de la société", "Item (type)": "Article (type)", "Asset Tag / Plate": "Étiquette / plaque", "Home Yard": "Dépôt d'attache", "Purchase Value": "Valeur d'achat", "Meter Reading": "Relevé compteur", "Meter Unit": "Unité compteur", Yard: "Dépôt", "Asset (specific unit)": "Équipement (unité spécifique)", Allocation: "Affectation", From: "Du", To: "Au", Purpose: "Objet", "Purchase Date": "Date d'achat", Notes: "Notes" }, Yl = { items: "Lignes", taxes: "Taxes", references: "Références de paiement", accounts: "Écritures de compte", payments: "Paiements", plans: "Articles du plan", monthly_distribution: "Répartition mensuelle" }, Jl = { "Create Sales Order": "Créer une commande client", "Create Sales Invoice": "Créer une facture de vente", "Create Delivery Note": "Créer un bon de livraison", "Create Purchase Invoice": "Créer une facture d'achat", "Create Purchase Receipt": "Créer un bon de réception", "Create Credit Note": "Créer un avoir", "Create Debit Note": "Créer une note de débit", "Create Return": "Créer un retour" }, Zl = { totalRevenue: "Chiffre d'affaires total", outstandingReceivable: "Créances en cours", outstandingPayable: "Dettes en cours", totalStockValue: "Valeur totale du stock", recentDocuments: "Documents récents", noRecentDocuments: "Aucun document récent" }, Xl = { title: "Chat Lambda ERP", subtitle: "Demandez-moi de créer des documents, de consulter des données ou d'exécuter des rapports.", s1: "Quels clients avons-nous ?", s2: "Affiche la balance comptable", s3: "Crée un devis pour 10 Bolt Pack M8", s4: "Liste toutes les factures impayées", loadOlder: "Charger les messages plus anciens", uploading: "Téléversement...", failed: "Échec", remove: "Supprimer", placeholderDemo: "Démo en direct en cours...", placeholderType: "Tapez un message...", placeholderConnecting: "Connexion...", attachTitle: "Joindre un fichier (PDF ou image, max 10 Mo)", creating: "Création du chat...", dropToAttach: "Déposez un fichier à joindre (images ou PDF, max 10 Mo chacun)", demoError: "Impossible de démarrer la démo", voiceStart: "Enregistrer un message vocal", voiceStop: "Arrêter l'enregistrement", voiceTranscribing: "Transcription...", micDenied: "L'accès au microphone a été refusé.", transcribeFailed: "Échec de la transcription. Veuillez réessayer." }, ec = /* @__PURE__ */ JSON.parse(`{"pageTitle":"Premiers pas avec Lambda ERP","intro":"Lambda ERP est un système ERP nativement IA, où toutes les fonctionnalités – comme créer des commandes, vérifier l'inventaire ou enregistrer des paiements – sont accessibles via une interface de chat. Il guide, raisonne et vérifie toutes vos tâches quotidiennes.","openChat":"Ouvrir le chat IA","tipLabel":"Astuce :","topTip":"Le chat IA peut aussi vous donner des liens directs vers des documents et des PDF téléchargeables. Demandez-lui simplement de créer ou de rechercher un document et il inclura des liens cliquables dans sa réponse.","manualIntro":"Si vous souhaitez comprendre pleinement le fonctionnement interne, ou préférez procéder manuellement, voici un cycle commercial complet étape par étape.","caTitle":"Analyses dynamiques – discutez pour obtenir n'importe quel graphique","caBadge":"Fonctionnalité phare","caBody":"Besoin d'un rapport que les modèles prédéfinis ne fournissent pas ? Décrivez-le simplement dans le chat. L'assistant appelle un modèle spécialiste du code qui écrit le rapport pour vous, l'exécute en direct sur vos données ERP et renvoie un lien partageable. Le graphique ou le tableau s'affiche en quelques secondes et est enregistré comme brouillon sous « Analyses personnalisées » dans la barre latérale – vous pouvez le rouvrir, partager l'URL avec vos collègues ou demander à l'assistant de l'affiner.","caTryAsking":"Essayez de demander (cliquez pour préremplir dans le chat) :","caPrompt1":"Montre-moi les 10 meilleurs clients par chiffre d'affaires cette année en diagramme à barres","caPrompt2":"Tendance mensuelle des achats par fournisseur sur tous les mois, étiquette l'axe des x avec le nom du fournisseur","caPrompt3":"Articles les plus vendus par quantité, avec un tableau et un graphique","caPrompt4":"Quels clients me doivent le plus en ce moment ?","caTryInChat":"Essayer dans le chat","caOpenWorkspace":"Ouvrir l'espace d'analyse","flowTitle":"Aperçu du flux des documents","salesCycle":"Cycle de vente","purchaseCycle":"Cycle d'achat","returns":"Retours","flowShortcut":"Raccourci : le devis peut ignorer la commande client et aller directement à la facture de vente ou au bon de livraison","legendStock":"impact stock","legendGl":"impact comptable","legendCash":"impact trésorerie","legendReversal":"annulation","lifecycleTitle":"Cycle de vie du document","lcDraftLabel":"Brouillon :","lcDraftDesc":"Modifiable. Aucun impact financier ni sur le stock.","lcSubmittedLabel":"Validé :","lcSubmittedDesc":"Verrouillé. Les écritures comptables et de stock sont générées.","lcCancelledLabel":"Annulé :","lcCancelledDesc":"Toutes les écritures sont inversées. Le document est archivé définitivement.","lcNoDelete":"Il n'y a pas de suppression. Pour annuler un brouillon : validez-le, puis annulez-le.","lcCorrect":"Pour corriger un document validé : annulez-le et créez-en un nouveau.","readyTitle":"Vous êtes prêt","readyBody1":"Vous comprenez maintenant le cycle complet. Un scénario réel typique ressemble à ceci : vous recevez une commande client (Devis → Commande client), vérifiez le stock, achetez ce dont vous avez besoin (Bon de commande → Bon de réception → Facture d'achat → Paiement), livrez au client (Bon de livraison), facturez-le (Facture de vente) et encaissez le paiement (Paiement). Si quelque chose revient, créez un avoir et un retour de bon de livraison pour inverser les finances et le stock. La Balance comptable confirme que tout est équilibré.","readyBody2":"Vous pouvez aussi tout faire via le chat IA en conversation naturelle – décrivez simplement ce dont vous avez besoin et il créera les bons documents pour vous.","chips":{"Quotation":"Devis","Sales Order":"Commande client","Delivery Note":"Bon de livraison","Sales Invoice":"Facture de vente","Payment Entry":"Paiement","Purchase Order":"Bon de commande","Purchase Receipt":"Bon de réception","Purchase Invoice":"Facture d'achat","Credit Note":"Avoir","DN Return":"Retour BL","Debit Note":"Note de débit","Draft":"Brouillon","Submitted":"Validé","Cancelled":"Annulé"},"links":{"Go to Setup":"Aller à la configuration","Customers":"Clients","Suppliers":"Fournisseurs","Items":"Articles","Warehouses":"Entrepôts","New Quotation":"Nouveau devis","View Quotations":"Voir les devis","Stock Balance":"Solde de stock","New Purchase Order":"Nouveau bon de commande","Purchase Receipts":"Bons de réception","Purchase Invoices":"Factures d'achat","Delivery Notes":"Bons de livraison","Sales Invoices":"Factures de vente","New Payment Entry":"Nouveau paiement","New Stock Entry":"Nouveau mouvement de stock","New Journal Entry":"Nouvelle écriture de journal","Profit & Loss":"Compte de résultat","Balance Sheet":"Bilan","AR Aging":"Balance âgée clients","AP Aging":"Balance âgée fournisseurs","Trial Balance":"Balance comptable","General Ledger":"Grand livre","Open AI Chat":"Ouvrir le chat IA"},"steps":{"1":{"title":"Configurer votre société","description":"Créez une société avec une devise de base. Cela génère votre plan comptable (30 comptes standard répartis entre Actif, Passif, Capitaux propres, Produits et Charges) ainsi qu'un centre de coûts par défaut. Vous pouvez aussi charger des données de démonstration avec des clients, fournisseurs et articles d'exemple pour démarrer rapidement."},"2":{"title":"Créer les données de base","description":"Avant de pouvoir effectuer des transactions, vous avez besoin de fiches de base. Créez au moins un client, un fournisseur, un article (avec un prix standard) et un entrepôt. Si vous avez chargé les données de démonstration à l'étape 1, elles existent déjà.","tip":"Les articles ont un prix standard qui se remplit automatiquement lorsque vous les ajoutez à des documents. Vous pouvez toujours modifier le prix par transaction."},"3":{"title":"Créer un devis","description":"Un devis est une offre non contraignante adressée à un client. Sélectionnez un client, ajoutez des lignes avec quantités et prix, éventuellement des lignes de taxe, puis enregistrez. Les devis n'ont aucun impact financier – ils ne créent ni écritures comptables ni mouvements de stock. Définissez une date de validité pour que l'offre expire automatiquement."},"4":{"title":"Valider et convertir en commande client","description":"Ouvrez votre devis enregistré et cliquez sur Valider pour le confirmer. Cliquez ensuite sur « Créer une commande client » pour le convertir. La commande client représente un engagement confirmé du client. Elle n'a toujours aucun impact financier, mais réserve du stock à des fins de planification.","tip":"Seuls les documents validés peuvent être convertis à l'étape suivante. Brouillon → Valider → Convertir est le flux standard. Pour les affaires rapides, vous pouvez ignorer la commande client et passer directement du devis à la facture de vente ou au bon de livraison."},"5":{"title":"Vérifier le stock avant l'exécution","description":"Avant de pouvoir livrer, vérifiez si vous avez réellement l'article en stock. Ouvrez le rapport Solde de stock et recherchez l'article. Si votre entrepôt affiche une quantité nulle, vous devrez d'abord approvisionner le stock – soit par un bon de commande (achat auprès d'un fournisseur), soit par un mouvement de stock (réception manuelle).","tip":"C'est un scénario courant dans la pratique : vous vendez quelque chose, puis réalisez que vous devez d'abord l'acheter. L'ERP gère les deux flux."},"6":{"title":"Cycle d'achat : acheter du stock à un fournisseur","description":"Si vous devez acheter du stock, vous avez deux chemins valides. Le chemin standard est Bon de commande → Bon de réception → Facture d'achat : utilisez-le lorsque les marchandises arrivent avant ou séparément de la facture du fournisseur. Si la facture et la réception arrivent ensemble, vous pouvez créer directement la facture d'achat et activer « Mettre à jour le stock » afin que le même document réceptionne le stock et enregistre la dette fournisseur.","tip":"Utilisez d'abord le bon de réception lorsque vous recevez les marchandises séparément. Utilisez la facture d'achat avec « Mettre à jour le stock » lorsqu'une seule étape doit à la fois réceptionner le stock et comptabiliser la facture fournisseur. Dans ce chemin direct, indiquez un entrepôt sur chaque ligne d'article en stock."},"7":{"title":"Créer un bon de livraison","description":"Maintenant que vous avez du stock, revenez à votre commande client validée et cliquez sur « Créer un bon de livraison ». Indiquez l'entrepôt sur chaque ligne d'article (d'où partent les marchandises), puis validez. Cela sort le stock de l'entrepôt – votre solde de stock diminue.","tip":"Le bon de livraison est le document d'expédition. Il réduit le stock mais ne crée pas de facture. Vous pouvez livrer et facturer séparément."},"8":{"title":"Créer et valider la facture de vente","description":"À partir de la commande client validée, créez une facture de vente. Lorsque vous validez la facture, des écritures comptables sont générées : les Clients (créances) sont débités (le client vous doit de l'argent) et les Produits des ventes crédités (revenu acquis). Si des taxes sont configurées, la TVA collectée est également créditée. Le montant dû indique ce que le client doit encore."},"9":{"title":"Enregistrer les paiements clients","description":"Créez un paiement pour enregistrer l'argent reçu du client. Définissez le type de paiement sur « Receive » (Recevoir), sélectionnez le client, indiquez le montant et affectez-le à la facture de vente. Vous pouvez effectuer des paiements partiels – le montant dû de la facture se met à jour en conséquence. Créez des paiements supplémentaires jusqu'à ce que la facture soit entièrement réglée.","tip":"Les paiements partiels sont courants. Une facture de 10 000 peut être payée 3 000 maintenant et 7 000 plus tard. Chaque paiement réduit le montant dû."},"10":{"title":"Mouvements de stock (inventaire manuel)","description":"Utilisez les mouvements de stock pour les mouvements d'inventaire non liés aux achats ou aux ventes. « Material Receipt » ajoute du stock (soldes d'ouverture, ajustements). « Material Issue » sort du stock (pertes, consommation interne). « Material Transfer » déplace du stock entre entrepôts. Chaque mouvement met à jour le livre de stock avec une valorisation au coût moyen pondéré.","tip":"Pour les marchandises achetées, utilisez des bons de réception plutôt que des mouvements de stock – ils sont liés au bon de commande et offrent une piste d'audit appropriée."},"11":{"title":"Écritures de journal","description":"Les écritures de journal sont des ajustements comptables manuels – charges à payer, corrections, reclassements, soldes d'ouverture. Chaque écriture doit avoir des débits et crédits équilibrés (total débit = total crédit). Utilisez-les lorsqu'aucun autre type de document ne convient."},"12":{"title":"Paiements des salaires","description":"Lambda ERP gère les salaires à l'aide des outils comptables existants. Comptabilisez d'abord la charge salariale : créez une écriture de journal qui débite la Charge salariale et crédite les Salaires à payer pour le montant total de la paie. Payez ensuite les employés : créez une autre écriture de journal (ou un paiement) qui débite les Salaires à payer et crédite votre compte bancaire. Ce processus en deux étapes garde vos comptes exacts – la charge est enregistrée dans la bonne période et la sortie de trésorerie est suivie séparément.","tip":"Vous pouvez demander à l'assistant IA de le faire pour vous : « Comptabilise 15 000 de salaires pour avril » suivi de « Paie les salaires d'avril depuis la banque ». Il créera automatiquement les bonnes écritures de journal."},"13":{"title":"Retours et avoirs","description":"Lorsqu'un client retourne des marchandises ou que vous devez émettre un avoir, créez un retour. Les retours utilisent le même type de document avec des quantités négatives. Ouvrez une facture de vente validée et créez un avoir (retour de facture de vente) – cela inverse les écritures comptables et réduit le montant dû de la facture d'origine. Pour le stock, créez un retour de bon de livraison pour réintégrer les marchandises en entrepôt. Côté achats, créez une note de débit (retour de facture d'achat) pour inverser une facture fournisseur, ou un retour de bon de réception pour renvoyer des marchandises.","tip":"Un avoir est simplement une facture de vente avec is_return=1 et des quantités négatives. La même logique comptable s'applique – les montants négatifs basculent automatiquement du bon côté débit/crédit. Pour un retour de vente complet, vous avez besoin à la fois d'un avoir (financier) et d'un retour de bon de livraison (stock)."},"14":{"title":"Exécuter les rapports","description":"Vérifiez vos comptes. Le Compte de résultat montre les produits face aux charges et le résultat net d'une période. Le Bilan montre votre situation financière (actif = passif + capitaux propres). La Balance âgée clients montre qui vous doit de l'argent et le retard. La Balance âgée fournisseurs montre ce que vous devez aux fournisseurs. La Balance comptable vérifie l'intégrité de la partie double. Le Grand livre montre chaque écriture individuelle. Le Solde de stock montre l'inventaire actuel."},"15":{"title":"Travailler en devises étrangères","description":"Vos comptes sont tenus dans une seule devise de base (choisie à la configuration de la société), mais vous pouvez effectuer des transactions dans n'importe quelle devise. Définissez une devise sur une facture ou un effet – ou attribuez une devise par défaut à un client ou un fournisseur – et le taux de change de cette date est recherché automatiquement et enregistré sur le document. Le document conserve ses montants dans sa propre devise, tandis que le Grand livre comptabilise toujours dans votre devise de base. Lorsque vous encaissez ou payez ultérieurement à un taux différent, le gain ou la perte de change réalisé est comptabilisé automatiquement sur un compte Gain/Perte de change. Vous pouvez même détenir un solde bancaire en devise étrangère et le convertir plus tard au taux de votre banque – l'écart par rapport à sa valeur comptable est alors réalisé. En fin de mois, vous pouvez réévaluer les soldes en devises ouverts au taux de clôture (un gain/perte latent qui s'inverse à la période suivante), et vous pouvez consulter n'importe quel état financier converti dans une autre devise pour l'affichage.","tip":"Le plus simple est de passer par le chat IA – essayez « Crée une facture de vente pour Lumiere Audio en EUR », « Montre-moi le bilan en EUR » ou « Quelle est notre exposition de change latente en fin de mois ? ». Les données de démonstration incluent déjà un client EUR (Lumiere Audio SARL) dont la facture a été encaissée à un taux différent, ainsi qu'une facture fournisseur EUR ouverte – ouvrez le Grand livre pour voir les écritures de change réalisées."}},"setupTitle":"Configurez votre entreprise — guidé par l'IA","setupBadge":"Commencez ici","setupBody":"Dites à l'assistant quel type d'entreprise vous dirigez, et il construit votre plan comptable pour vous — adapté à votre secteur, avec les bons comptes et des valeurs par défaut cohérentes déjà en place. Il explique chaque décision et ne crée quoi que ce soit qu'après votre approbation.","setupSectors":"Adapté aux services, au commerce de détail & POS, à l'hôtellerie-restauration, au commerce de gros & distribution, à l'import/export, à la fabrication et à la construction.","setupGetStarted":"Commencer dans le chat","setupManual":"Vous préférez un formulaire manuel ? Utilisez la configuration classique.","setupManualLink":"Configuration classique","setupPrompt":"Je souhaite configurer mon entreprise. Pouvez-vous me guider ?"}`), tc = { title: "Aide au formatage", intro: "Le texte des Notes / Conditions prend en charge une mise en forme légère sur le PDF :", heading: "titre en gras", emphasis: "italique / gras", rule: "ligne de séparation horizontale", price: "prix aligné à droite à côté du texte ci-dessus (p. ex. Mensuel | CHF 380.—)", block: "Séparez les blocs par une ligne vide." }, nc = { period: "Période", allTime: "Toute la période", semantics: "Comptes de bilan : solde à la fin de la période. Comptes de résultat : mouvement de la période. Cliquez sur un compte pour son grand livre.", empty: "Aucun compte trouvé. Terminez d'abord la configuration de l'entreprise.", disabled: "désactivé", rootType: { Asset: "Actifs", Liability: "Passifs", Equity: "Capitaux propres", Income: "Produits", Expense: "Charges" } }, ac = { fleetCalendar: "Calendrier de flotte", yard: "Dépôt", machineType: "Type de machine", prev: "Préc.", today: "Aujourd'hui", next: "Suiv.", weeks: "semaines", newBooking: "Nouvelle réservation", reserved: "Réservé", onHire: "En location", machine: "Machine", loadError: "Impossible de charger le calendrier de flotte.", noAssets: "Aucun équipement à afficher. Ajoutez des machines sous Locations → Flotte.", pooledNote: "{{count}} réservation(s) en attente d’attribution", unassigned: "Pool — non attribué", poolWarning: "La capacité est réservée. Aucune machine précise n’est encore attribuée ; attribuez une machine avant la sortie." }, rc = { Unit: "Machine spécifique", Pool: "Pool – machine à affecter", Available: "Disponible", "On Hire": "En location", Out: "Sorti", Maintenance: "Maintenance", Retired: "Retiré", Reserved: "Réservé", Returned: "Retourné", Cancelled: "Annulé", Customer: "Client", Supplier: "Fournisseur" }, ic = { asset: { item_code: "L'article est le TYPE de machine et porte le tarif ; un équipement en est une unité physique. L'article doit avoir is_asset_tracked = 1.", asset_tag: "Plaque ou numéro de série — doit être unique sur tous les équipements.", status: "État actuel de l'unité (pas une période). Les unités retirées quittent le pool.", meter_reading: "Heures de fonctionnement / km." }, reservation: { item_code: "Réservation de pool ('une pelle de 1,7 t') : renseigner Article + Dépôt + Qté. Pour une machine précise, renseigner Équipement.", asset: "Réserver une machine précise. Une réservation d'unité consomme aussi une place du pool Article+Dépôt.", from_datetime: "Début exact — date et heure (p. ex. 14:27). Une date seule signifie 00:00.", to_datetime: "Fin exacte. Les fenêtres sont semi-ouvertes [de, à) : une location finissant à 09:00 et la suivante commençant à 09:00 ne se chevauchent PAS." } }, sc = { label: "Taxe de vente par défaut", none: "Aucune application automatique", hint: "Appliquée aux nouveaux devis, commandes et factures si les taxes ne sont pas explicitement définies. Les documents existants restent inchangés. Cliquez sur Enregistrer pour appliquer.", saveFirst: "Enregistrez d’abord l’entreprise, puis sélectionnez un de ses modèles de taxes de vente.", loadError: "Impossible de charger les modèles de taxes. La sélection enregistrée est conservée.", retry: "Réessayer", unavailable: "Le modèle enregistré n’est pas disponible comme modèle de taxes de vente pour cette entreprise. Vérifiez la sélection.", empty: "Aucun modèle de taxes de vente n’est encore défini pour cette entreprise." }, oc = { title: "Connecter une application", subtitle: "Choisissez où utiliser Lambda ERP. Nous afficherons la configuration adaptée.", other: "Autre application", customName: "Intégration personnalisée", signIn: "Se connecter", apiKey: "Clé API", access: "Accès", role_viewer: "Consulter les données", role_manager: "Créer et modifier", role_admin: "Accès administratif complet", roleHelp: "L’accès est toujours limité par vos propres droits ERP. Accordez uniquement ce dont cette application a besoin.", copied: "Copié", copyFailed: "La copie automatique a échoué. Sélectionnez le texte et copiez-le manuellement.", retry: "Réessayer", interface: "Type de connexion", chatDisabled: "L’accès à l’API de chat est désactivé. Demandez à un administrateur de l’activer dans les paramètres API ci-dessus.", restDisabled: "L’accès REST API / MCP est désactivé. Un administrateur doit l’activer dans les paramètres API avant de connecter cette application.", chatgptStep: "Ajoutez une application / un connecteur personnalisé dans ChatGPT. Le mode développeur et une autorisation de l’espace de travail peuvent être nécessaires.", claudeStep: "Dans Claude, ouvrez « Connectors » puis « Add custom connector ».", urlStep: "Collez cette URL de serveur. Utilisez la connexion OAuth ; laissez les champs facultatifs Client ID et Client Secret vides.", approveStep: "Connectez l’application, identifiez-vous dans Lambda ERP et approuvez l’accès souhaité.", noKeyNeeded: "Aucune clé API ni en-tête personnalisé n’est nécessaire. Choisissez l’accès sur l’écran d’autorisation.", officialGuide: "Ouvrir le guide de configuration", copyUrl: "Copier l’URL du serveur", keyReady: "Votre clé de connexion est prête", copySetup: "Copier la configuration", copyHeader: "Copier l’en-tête d’autorisation", cliStep: "Exécutez cette commande dans votre terminal, puis vérifiez la connexion avec /mcp dans Claude Code.", codexStep: "Ajoutez cette entrée à votre configuration Codex, puis redémarrez Codex et vérifiez /mcp.", done: "Terminé — masquer la clé", existing: "Connexions existantes", refresh: "Actualiser", lastUsed: "Dernière utilisation", notUsed: "Pas encore utilisée", consentTitle: "Se connecter à Lambda ERP", expired: "Cette demande de connexion est absente ou expirée. Recommencez depuis votre application.", loginFirst: "Connectez-vous à votre compte ERP pour examiner la demande de cette application.", consentBody: "« {{app}} » demande l’accès à votre ERP. Choisissez l’accès que vous souhaitez autoriser.", returnTo: "Après autorisation, vous retournerez vers", revokeHelp: "Vous pouvez révoquer cette connexion à tout moment dans Paramètres → Accès API.", deny: "Annuler", allow: "Autoriser la connexion", explain_viewer: "L’application peut lire les données et rapports ERP et générer des PDF. Elle ne peut pas modifier les données métier.", explain_manager: "L’application peut aussi créer, modifier, valider et annuler des documents et mettre à jour les données de base.", explain_admin: "L’application peut utiliser tous les outils ERP disponibles, y compris les actions administratives et la suppression de données de base." }, lc = {
8177
+ }, Bl = { incomplete: "Brouillon enregistré. Le PDF nécessite un client, une entreprise et au moins une offre." }, Il = { save: "Enregistrer", saving: "Enregistrement...", submit: "Valider", cancel: "Annuler", cancelDoc: "Annuler", cancelDocAbort: "Ne pas annuler", cancelDocConfirmBtn: "Annuler définitivement", cancelBooking: "Annuler la réservation", cancelBookingConfirm: "Oui, annuler", keepBooking: "Conserver", discardDraft: "Supprimer le brouillon", keepDraft: "Conserver", discardDraftConfirm: "Supprimer", showDiscarded: "Afficher les supprimés", showDisabled: "Afficher les désactivés", columns: "Colonnes", searchPlaceholder: "Rechercher…", noRecords: "Aucun enregistrement trouvé", active: "Actif", disabled: "Désactivé", sortByColumn: "Trier par cette colonne", delete: "Supprimer", edit: "Modifier", pdf: "PDF", addRow: "Ajouter une ligne", loading: "Chargement...", enable: "Activer", disable: "Désactiver", refresh: "Actualiser", refreshing: "Actualisation", areYouSure: "Êtes-vous sûr ?", all: "Tous", apply: "Appliquer", new: "Nouveau", prev: "Précédent", next: "Suivant", perPage: "Par page", page: "Page", of: "sur", showing: "Affichage", total: "Total", totals: "Totaux", errorOccurred: "Une erreur s'est produite" }, El = { today: "Aujourd’hui", thisWeek: "Cette semaine", thisMonth: "Ce mois-ci", thisQuarter: "Ce trimestre", thisYear: "Cette année", lastMonth: "Mois dernier", lastQuarter: "Trimestre dernier", lastYear: "Année dernière", endOfThisMonth: "Fin du mois en cours", endOfLastMonth: "Fin du mois dernier", endOfThisQuarter: "Fin du trimestre en cours", endOfThisYear: "Fin de l’année en cours", endOfLastYear: "Fin de l’année dernière" }, Ll = { title: "Importer un relevé bancaire", subtitle: "Prévisualisez et importez des relevés ISO 20022 CAMT sans écriture comptable automatique.", selectFiles: "Fichiers CAMT", chooseFiles: "Choisir des fichiers XML ou ZIP", dropFiles: "Déposez les fichiers XML ou ZIP ici", unsupportedFiles: "Seuls les fichiers CAMT XML ou ZIP sont acceptés : {{files}}", fileHint: "camt.053.001.04 et camt.053.001.08 · plusieurs fichiers acceptés", preview: "Afficher l’aperçu", noPosting: "L’aperçu et l’importation ne génèrent aucune écriture comptable.", alreadyImported: "Déjà importé", period: "Période", openingBalance: "Solde initial", closingBalance: "Solde final", entries: "Opérations", credits: "Crédits", debits: "Débits", details: "Détails des transactions", batches: "lots", qrReferences: "Références QR", mapping: "Affectation du compte bancaire", selectBankAccount: "Sélectionner un compte bancaire…", noBankAccounts: "Aucun compte bancaire n’a encore été configuré.", createBankAccount: "Configurer un compte bancaire", mappingRequired: "Affectez chaque nouveau relevé à un compte bancaire avant l’importation.", import: "Importer les relevés", importComplete: "Importation terminée", importCounts: "{{imported}} opérations importées, {{duplicates}} doublons ignorés", history: "Importations récentes", importId: "Importation", bankAccount: "Compte bancaire", source: "Fichier source", openReconciliation: "Ouvrir le rapprochement bancaire" }, Ol = { title: "Rapprochement bancaire", subtitle: "Rapprochez les transactions importées avec des écritures, des factures ouvertes ou des comptes comptables.", status: "Statut", unreconciled: "Non rapprochée", reconciled: "Rapprochée", transactions: "Transactions bancaires", empty: "Aucune transaction bancaire dans cette vue.", remittance: "Information de paiement", reference: "Référence", openTransaction: "Ouvrir la transaction bancaire", conversionRate: "Taux de change (1 {{currency}} en {{base}})", conversionRateHint: "Requis si aucun taux de change n’existe à la date de la transaction.", existingVouchers: "Écritures existantes", existingVouchersHint: "Ces pièces validées contiennent un mouvement exact sur ce compte bancaire. Le rapprochement ne les comptabilise pas une seconde fois.", match: "Rapprocher", openInvoices: "Factures ouvertes", openInvoicesHint: "Sélectionnez une ou plusieurs factures du même tiers. La confirmation crée et valide un paiement.", outstanding: "Solde ouvert", allocation: "Affectation", noInvoiceSuggestions: "Aucune facture ouverte plausible n’a été trouvée.", allocated: "{{amount}} affectés sur {{total}}", createPayment: "Créer le paiement et rapprocher", directPosting: "Comptabiliser sur un compte", directPostingHint: "Pour les frais, intérêts et autres mouvements sans facture. Les comptes clients, fournisseurs, bancaires et de caisse sont volontairement exclus.", counterpartyAccount: "Compte de contrepartie", remarks: "Remarque", createJournal: "Créer l’écriture et rapprocher", activeMatch: "Rapprochement actif", undo: "Annuler le rapprochement", confirmExistingTitle: "Rapprocher l’écriture existante ?", confirmExistingBody: "Rapprocher {{transaction}} avec {{voucher}} ? Aucune nouvelle écriture ne sera comptabilisée.", confirmGroupBody: "Rapprocher les {{count}} transactions, pour un total de {{amount}}, avec {{voucher}} ? Aucune nouvelle écriture ne sera comptabilisée.", groupedTransactions: "{{count}} transactions groupées", confirmPaymentTitle: "Comptabiliser le paiement et rapprocher ?", confirmPaymentBody: "Cette action valide un paiement de {{amount}} pour {{transaction}} et l’affecte à {{count}} facture(s).", onAccountWarning: "{{amount}} resteront comme paiement non affecté pour ce tiers.", confirmJournalTitle: "Comptabiliser l’écriture et rapprocher ?", confirmJournalBody: "Cette action comptabilise {{amount}} pour {{transaction}} sur {{account}}.", postAndReconcile: "Comptabiliser et rapprocher", undoTitle: "Annuler le rapprochement ?", undoCreatedBody: "La pièce créée par ce rapprochement sera annulée, ses écritures extournées et les affectations aux factures restaurées.", undoExistingBody: "La pièce existante sera uniquement dissociée. Elle restera validée et ses écritures ne seront pas modifiées.", undoExistingGroupBody: "Les {{count}} transactions groupées ({{transactions}}) seront toutes dissociées ensemble. La pièce existante restera validée et ses écritures ne seront pas modifiées.", reconciledNotice: "Transaction bancaire rapprochée.", groupReconciledNotice: "{{count}} transactions bancaires ont été rapprochées en groupe.", undoneNotice: "Rapprochement annulé." }, Fl = { placeholder: "Rechercher ou saisir champ:valeur…", search: "Rechercher", addFilter: "+ Filtre", availableFields: "Champs disponibles", fieldHelp: "Choisissez un champ ou saisissez son nom technique suivi de deux-points.", field: "Champ", valueFor: "Valeur pour {{label}}", allFields: "Tous les champs", typeForValues: "Commencez à saisir pour rechercher des valeurs dans ce champ.", noValues: "Aucune valeur correspondante.", noFields: "Aucun champ correspondant.", useValue: "Utiliser « {{value}} »", removeFilter: "Supprimer le filtre {{label}}", types: { text: "Texte", textarea: "Texte long", select: "Sélection", link: "Enregistrement lié", date: "Date", datetime: "Date et heure", number: "Nombre", currency: "Montant" } }, zl = { title: "Langue", help: "Choisissez la langue de l'interface. Votre choix est enregistré dans ce navigateur.", label: "Langue" }, Ml = { back: "Retour", logout: "Déconnexion", openMenu: "Ouvrir le menu", closeMenu: "Fermer le menu", new: "Nouveau : {{label}}" }, ql = { dashboard: "Tableau de bord", setup: "Configuration de l'entreprise", tutorial: "Premiers pas", chat: "Chat", reports: "Rapports", "trial-balance": "Balance comptable", "general-ledger": "Grand livre", "stock-balance": "Solde de stock", "profit-and-loss": "Compte de résultat", "balance-sheet": "Bilan", "ar-aging": "Balance âgée clients", "ap-aging": "Balance âgée fournisseurs", analytics: "Analyses", "chart-of-accounts": "Plan comptable" }, $l = { quotation: { one: "Devis", other: "Devis" }, proposal: { one: "Offre groupée", other: "Offres groupées" }, "sales-order": { one: "Commande client", other: "Commandes client" }, "sales-invoice": { one: "Facture de vente", other: "Factures de vente" }, "pos-invoice": { one: "Ticket de caisse", other: "Tickets de caisse" }, "purchase-order": { one: "Bon de commande", other: "Bons de commande" }, "purchase-invoice": { one: "Facture d'achat", other: "Factures d'achat" }, "payment-entry": { one: "Paiement", other: "Paiements" }, "journal-entry": { one: "Écriture comptable", other: "Écritures comptables" }, "bank-transaction": { one: "Transaction bancaire", other: "Transactions bancaires" }, "bank-account": { one: "Compte bancaire", other: "Comptes bancaires" }, budget: { one: "Budget", other: "Budgets" }, subscription: { one: "Abonnement", other: "Abonnements" }, "stock-entry": { one: "Mouvement de stock", other: "Mouvements de stock" }, "delivery-note": { one: "Bon de livraison", other: "Bons de livraison" }, "purchase-receipt": { one: "Bon de réception", other: "Bons de réception" }, "pricing-rule": { one: "Règle de prix", other: "Règles de prix" } }, Vl = { company: { one: "Société", other: "Sociétés" }, customer: { one: "Client", other: "Clients" }, supplier: { one: "Fournisseur", other: "Fournisseurs" }, item: { one: "Article", other: "Articles" }, warehouse: { one: "Entrepôt", other: "Entrepôts" } }, Ul = { chats: "Discussions", newChat: "Nouvelle discussion", deleteChat: "Supprimer la discussion", customAnalytics: "Analyses personnalisées", newAnalytics: "Nouvelle analyse", deleteReport: "Supprimer le rapport", groups: { Introduction: "Introduction", Selling: "Ventes", Buying: "Achats", Accounting: "Comptabilité", Stock: "Stock", Reports: "Rapports", Masters: "Données de base", Settings: "Paramètres", Rentals: "Locations" }, items: { "Getting Started": "Premiers pas", "Company Setup": "Configuration de l'entreprise", "Opening Balances": "Soldes d'ouverture", Quotation: "Devis", Proposal: "Offre groupée", "Sales Order": "Commande client", "Sales Invoice": "Facture de vente", "POS Invoice": "Ticket de caisse", "Purchase Order": "Bon de commande", "Purchase Invoice": "Facture d'achat", "Payment Entry": "Paiement", "Journal Entry": "Écriture comptable", "Bank Transaction": "Transaction bancaire", "Bank Account": "Comptes bancaires", "Bank Statement Import": "Importer un relevé bancaire", "Bank Reconciliation": "Rapprochement bancaire", Budget: "Budget", Subscription: "Abonnement", "Stock Entry": "Mouvement de stock", "Delivery Note": "Bon de livraison", "Purchase Receipt": "Bon de réception", "Trial Balance": "Balance comptable", "Profit & Loss": "Compte de résultat", "Balance Sheet": "Bilan", "General Ledger": "Grand livre", "AR Aging": "Balance âgée clients", "AP Aging": "Balance âgée fournisseurs", Analytics: "Analyses", "Stock Balance": "Solde de stock", Company: "Société", Customer: "Client", Supplier: "Fournisseur", Item: "Article", Warehouse: "Entrepôt", General: "Général", "Pricing Rule": "Règle de prix", "Users & Team": "Utilisateurs et équipe", "Chart of Accounts": "Plan comptable", "Fleet Calendar": "Calendrier de flotte", "Fleet (Assets)": "Flotte (matériel)", Reservations: "Réservations" } }, Kl = { taglineFirstRun: "Créez votre compte administrateur pour commencer", taglineRegister: "Créez votre compte", taglineSignIn: "Connectez-vous à votre compte", demoBanner: "Le mode démo est actif. Connectez-vous avec un compte administrateur pour gérer les paramètres ou désactiver l'accès public.", fullName: "Nom complet", email: "E-mail", password: "Mot de passe", confirmPassword: "Confirmer le mot de passe", createAdmin: "Créer un compte administrateur", register: "S'inscrire", signIn: "Se connecter", browsingTitle: "Simple visite ?", browsingBody: "Démarrez une nouvelle session de chat et regardez la démo en direct.", enterDemo: "Lancer la démo", haveInvite: "Vous avez une invitation ?", noAccountPrompt: "Besoin d'un compte ?", registerLink: "S'inscrire", alreadyHaveAccount: "Vous avez déjà un compte ?", signInLink: "Se connecter", passwordsNoMatch: "Les mots de passe ne correspondent pas", passwordTooShort: "Le mot de passe doit comporter au moins 6 caractères", registrationFailed: "Échec de l'inscription", loginFailed: "Échec de la connexion", orContinueWith: "ou continuer avec", continueWithProvider: "Continuer avec {{provider}}", oauthError: { generic: "Échec de la connexion. Veuillez réessayer.", email_exists: "Un compte avec cet e-mail existe déjà. Connectez-vous, puis liez ce fournisseur depuis les paramètres du compte.", email_unverified: "Votre fournisseur n'a pas confirmé d'e-mail vérifié, la connexion est donc impossible.", registration_closed: "Cet e-mail n'a pas encore de compte et l'inscription se fait uniquement sur invitation.", invite_invalid: "Cette invitation est invalide, déjà utilisée ou émise pour un autre e-mail.", identity_taken: "Ce compte fournisseur est déjà lié à un autre utilisateur.", account_disabled: "Ce compte a été désactivé.", token_exchange_failed: "Échec de la connexion auprès du fournisseur. Veuillez réessayer." } }, jl = { pdfTitle: "PDF et impression", pageSize: "Format de page", pdfHelp: "A4 est la norme internationale (210 x 297 mm). Letter est la norme aux États-Unis (8,5 x 11 po).", passwordTitle: "Changer le mot de passe", currentPassword: "Mot de passe actuel", newPassword: "Nouveau mot de passe", confirmNewPassword: "Confirmer le nouveau mot de passe", changePassword: "Changer le mot de passe", changingPassword: "Modification...", passwordChanged: "Mot de passe modifié.", setPasswordTitle: "Définir un mot de passe", setPasswordBody: "Votre compte se connecte avec Google. Définissez un mot de passe pour vous connecter aussi avec e-mail et mot de passe — un recours pratique si vous perdez l'accès à votre compte Google.", setPassword: "Définir le mot de passe", passwordSet: "Mot de passe défini.", linkedAccountsTitle: "Comptes liés", linkedAccountsBody: "Liez un compte Google ou Apple pour vous connecter sans mot de passe.", linked: "Lié", linkProvider: "Lier {{provider}}", openingTitle: "Soldes d'ouverture", openingBody: "La page Soldes d'ouverture permet d'importer les soldes des comptes, le stock et les factures impayées d'un système précédent.", openingEnabled: "Actuellement activé — accessible sous Introduction > Soldes d'ouverture.", openingDisabled: "Actuellement désactivé — la page est masquée dans la barre latérale.", signupTitle: "Inscription publique", signupBody: "Permettre à quiconque de créer son propre compte (en tant que lecteur) sans invitation. Lorsque désactivé, les nouveaux utilisateurs ne peuvent rejoindre que via un lien d'invitation.", signupEnabled: "Actuellement activé — toute personne disposant de l'URL peut s'inscrire en tant que lecteur.", signupDisabled: "Actuellement désactivé — l'inscription se fait uniquement sur invitation.", chatApiTitle: "API de chat", apiTitle: "API programmatique", restApiBody: "Autoriser les applications et scripts à accéder à cet ERP via REST ou MCP, avec des clés API ou une connexion OAuth. Désactivé par défaut.", restApiEnabled: "Actuellement activé — les clés émises peuvent appeler /api/documents, /api/masters et le reste de l'API REST.", restApiDisabled: "Actuellement désactivé — l'API REST n'accepte que le cookie de session ; les clés sont rejetées.", restApiDeactivateTitle: "Désactiver l'API REST ?", restApiDeactivateBody: "L'accès par clé Bearer à l'API REST sera désactivé — les connecteurs et scripts sont rejetés jusqu'à sa réactivation. L'application web (cookie de session) n'est pas affectée, et vos clés API ne sont PAS supprimées : elles fonctionnent à nouveau dès que vous réactivez l'API.", chatApiBody: "Permettre à des applications externes de dialoguer avec l'agent de chat de cet ERP via une API HTTP, à l'aide de clés API Bearer. Désactivé par défaut.", chatApiEnabled: "Actuellement activé — les clés émises peuvent appeler POST /api/v1/chat.", chatApiDisabled: "Actuellement désactivé — l'API renvoie 404 et les clés ne fonctionnent pas.", chatApiDeactivateTitle: "Désactiver l'API chat ?", chatApiDeactivateBody: "L'API chat sera désactivée — toute requête programmatique (clés API Bearer) est rejetée jusqu'à sa réactivation. Vos clés API ne sont PAS supprimées : elles restent enregistrées et fonctionnent à nouveau dès que vous réactivez l'API.", chatApiDeactivateConfirm: "Désactiver l'API", chatApiKeysTitle: "Applications et clés API", chatApiTokenOnce: "Copiez ce jeton maintenant — il ne sera plus affiché.", chatApiCopy: "Copier", chatApiDismiss: "Fermer", chatApiRevoked: "révoquée", chatApiRevoke: "Révoquer", chatApiNoKeys: "Aucune connexion pour le moment.", chatApiName: "Nom", chatApiRole: "Rôle", chatApiCreate: "Créer une clé", chatApiWarn: "Une clé agit avec votre identité et au plus votre rôle — à traiter comme un mot de passe. Révoquer d'abord, puis supprimer.", publicTitle: "Accès public (mode démo)", publicActive: "Actif", publicActiveBody: "L'accès public est activé. N'importe qui peut utiliser l'application sans se connecter. Tous les visiteurs obtiennent des autorisations de niveau gestionnaire.", disablePublic: "Désactiver l'accès public", disabling: "Désactivation...", publicInactiveBody: "Activez l'accès public pour permettre à quiconque d'utiliser l'application sans compte. Utile pour les démos et présentations. Tous les visiteurs obtiennent des autorisations de niveau gestionnaire et partagent la même identité.", enablePublic: "Activer l'accès public", enableWarning: "Cela permettra à quiconque d'accéder à l'application sans se connecter. Ils pourront créer, modifier et valider des documents. Votre compte administrateur nécessite toujours une connexion.", yesEnable: "Oui, activer", enabling: "Activation...", tokenSpendTitle: "Dépenses de jetons", chatApiDelete: "Supprimer", chatApiYourKeys: "Vos connexions", chatApiYouBadge: "vous", chatApiUnknownUser: "Utilisateur inconnu", chatApiAdminAllNote: "En tant qu’administrateur, vous pouvez gérer les connexions de tous les utilisateurs. Les vôtres apparaissent en premier.", chatApiConfirmTitle: "Cette connexion appartient à un autre utilisateur", chatApiConfirmRevokeBody: "« {{key}} » appartient à {{name}}. La révocation déconnectera immédiatement son application. Continuer ?", chatApiConfirmDeleteBody: "« {{key}} » appartient à {{name}}. Supprimer définitivement cette connexion révoquée ?", chatApiPersonal: "Connectez des applications à votre compte ERP. L’accès ne dépasse jamais vos droits actuels.", version: "Version {{version}}" }, Wl = { Draft: "Brouillon", Open: "Ouvert", Submitted: "Validé", "To Deliver and Bill": "À livrer et facturer", "To Deliver": "À livrer", "To Bill": "À facturer", Completed: "Terminé", Ordered: "Commandé", Paid: "Payé", Unpaid: "Impayé", Cancelled: "Annulé", Discarded: "Supprimé", Overdue: "En retard", Return: "Retour", Closed: "Clôturé" }, Gl = { periodRange: "Période du rapport : {{from}} – {{to}}", periodFrom: "Période du rapport à partir du {{from}}", periodThrough: "Période du rapport jusqu’au {{to}}", asOfDate: "Date de référence : {{date}}", balanced: "ÉQUILIBRÉ", imbalanced: "DÉSÉQUILIBRÉ", income: "Produits", expenses: "Charges", totalIncome: "Total des produits", totalExpenses: "Total des charges", netProfit: "Bénéfice", netLoss: "Perte", assets: "Actif", liabilities: "Passif", equity: "Capitaux propres", totalAssets: "Total de l'actif", totalLiabilities: "Total du passif", totalEquity: "Total des capitaux propres", totalLiabilitiesEquity: "Total passif + capitaux propres", current: "Non échu", noData: "Aucune donnée trouvée", noEntries: "Aucune écriture trouvée", noStockData: "Aucune donnée de stock trouvée", noReceivables: "Aucune créance en cours", noPayables: "Aucune dette en cours", noDocuments: "Aucun document trouvé" }, Hl = { required: "Requis : {{fields}}", deleteConfirm: "Supprimer ce {{label}} ?", disabledNotice: "Ce {{label}} est désactivé. Il reste dans le système car d'autres enregistrements y font référence.", disabledOnDelete: "{{label}} {{name}} a été désactivé au lieu d'être supprimé car il est référencé par {{reason}}." }, Ql = { "Last modified": "Dernière modification", Created: "Créé", Account: "Compte", Amount: "Montant", Balance: "Solde", Credit: "Crédit", Debit: "Débit", Date: "Date", Party: "Tiers", "Party Type": "Type de tiers", "Voucher Type": "Type de pièce", "Voucher No": "N° de pièce", Invoice: "Facture", Customer: "Client", Supplier: "Fournisseur", "Due Date": "Date d'échéance", Outstanding: "Encours", "Outstanding Amount": "Montant dû", "Item Code": "Code article", "Item Name": "Nom de l'article", Item: "Article", Warehouse: "Entrepôt", Qty: "Qté", "Valuation Rate": "Coût unitaire", "Stock Value": "Valeur du stock", Name: "Nom", Status: "Statut", Company: "Société", "From Date": "Date de début", "To Date": "Date de fin", "As of Date": "À la date", "Valid Till": "Valable jusqu'au", "Net Total": "Total net", Tax: "Taxe", "Grand Total": "Total TTC", Frequency: "Fréquence", "Notes / Terms": "Notes / Conditions", "Posting Date": "Date de comptabilisation", "Transaction Date": "Date de transaction", "Delivery Date": "Date de livraison", "% Delivered": "% livré", "% Billed": "% facturé", "Update Stock": "Mettre à jour le stock", "Payment Type": "Type de paiement", "Paid From": "Payé depuis", "Paid To": "Payé à", "Paid Amount": "Montant payé", Type: "Type", Total: "Total", Allocated: "Affecté", Unallocated: "Non affecté", Remark: "Remarque", "Total Debit": "Total débit", "Total Credit": "Total crédit", "Cost Center": "Centre de coûts", "Source Warehouse": "Entrepôt source", "Target Warehouse": "Entrepôt cible", Source: "Source", Target: "Cible", "Incoming Value": "Valeur entrante", "Outgoing Value": "Valeur sortante", Rate: "Prix unitaire", "Income Account": "Compte de produits", "Expense Account": "Compte de charges", Description: "Description", "Rate (%)": "Taux (%)", Mode: "Mode", Change: "Monnaie rendue", Currency: "Devise", Title: "Titre", Selling: "Vente", Buying: "Achat", "Discount %": "Remise %", "Discount Amt": "Montant remise", "Min Qty": "Qté min", "Valid From": "Valable à partir du", "Valid Upto": "Valable jusqu'au", Priority: "Priorité", Enabled: "Activé", "Fiscal Year": "Exercice", "Budget Amount": "Montant du budget", "If Exceeded": "Si dépassé", Month: "Mois", "Bank Account": "Compte bancaire", Deposit: "Dépôt", Withdrawal: "Retrait", "Reference No": "N° de référence", "Matched Type": "Type rapproché", "Matched Doc": "Pièce rapprochée", "Start Date": "Date de début", "End Date": "Date de fin", "Billing Interval": "Intervalle de facturation", "Current Period Start": "Début période actuelle", "Current Period End": "Fin période actuelle", "Plan Items": "Articles du plan", "Total Amount": "Montant total", "Stock Entry Type": "Type de mouvement de stock", "Rate Or Discount": "Prix ou remise", "Discount Percentage": "Pourcentage de remise", ID: "ID", "Customer Name": "Nom du client", "Customer Group": "Groupe de clients", Territory: "Territoire", "Credit Limit": "Limite de crédit", Email: "E-mail", Phone: "Téléphone", Address: "Adresse", City: "Ville", "Zip Code": "Code postal", Country: "Pays", "Tax ID": "N° de TVA", Website: "Site web", "Contact Person": "Personne de contact", "Contact Email": "E-mail du contact", "Contact Phone": "Téléphone du contact", "Supplier Name": "Nom du fournisseur", "Supplier Group": "Groupe de fournisseurs", "Item Group": "Groupe d'articles", "Stock UOM": "Unité de stock", "Standard Rate": "Prix standard", "Warehouse Name": "Nom de l'entrepôt", "Company Name": "Nom de la société", "Item (type)": "Article (type)", "Asset Tag / Plate": "Étiquette / plaque", "Home Yard": "Dépôt d'attache", "Purchase Value": "Valeur d'achat", "Meter Reading": "Relevé compteur", "Meter Unit": "Unité compteur", Yard: "Dépôt", "Asset (specific unit)": "Équipement (unité spécifique)", Allocation: "Affectation", From: "Du", To: "Au", Purpose: "Objet", "Purchase Date": "Date d'achat", Notes: "Notes" }, Yl = { items: "Lignes", taxes: "Taxes", references: "Références de paiement", accounts: "Écritures de compte", payments: "Paiements", plans: "Articles du plan", monthly_distribution: "Répartition mensuelle" }, Jl = { "Create Sales Order": "Créer une commande client", "Create Sales Invoice": "Créer une facture de vente", "Create Delivery Note": "Créer un bon de livraison", "Create Purchase Invoice": "Créer une facture d'achat", "Create Purchase Receipt": "Créer un bon de réception", "Create Credit Note": "Créer un avoir", "Create Debit Note": "Créer une note de débit", "Create Return": "Créer un retour" }, Zl = { totalRevenue: "Chiffre d'affaires total", outstandingReceivable: "Créances en cours", outstandingPayable: "Dettes en cours", totalStockValue: "Valeur totale du stock", recentDocuments: "Documents récents", noRecentDocuments: "Aucun document récent" }, Xl = { title: "Chat Lambda ERP", subtitle: "Demandez-moi de créer des documents, de consulter des données ou d'exécuter des rapports.", s1: "Quels clients avons-nous ?", s2: "Affiche la balance comptable", s3: "Crée un devis pour 10 Bolt Pack M8", s4: "Liste toutes les factures impayées", loadOlder: "Charger les messages plus anciens", uploading: "Téléversement...", failed: "Échec", remove: "Supprimer", placeholderDemo: "Démo en direct en cours...", placeholderType: "Tapez un message...", placeholderConnecting: "Connexion...", attachTitle: "Joindre un fichier (PDF ou image, max 10 Mo)", creating: "Création du chat...", dropToAttach: "Déposez un fichier à joindre (images ou PDF, max 10 Mo chacun)", demoError: "Impossible de démarrer la démo", voiceStart: "Enregistrer un message vocal", voiceStop: "Arrêter l'enregistrement", voiceTranscribing: "Transcription...", micDenied: "L'accès au microphone a été refusé.", transcribeFailed: "Échec de la transcription. Veuillez réessayer." }, ec = /* @__PURE__ */ JSON.parse(`{"pageTitle":"Premiers pas avec Lambda ERP","intro":"Lambda ERP est un système ERP nativement IA, où toutes les fonctionnalités – comme créer des commandes, vérifier l'inventaire ou enregistrer des paiements – sont accessibles via une interface de chat. Il guide, raisonne et vérifie toutes vos tâches quotidiennes.","openChat":"Ouvrir le chat IA","tipLabel":"Astuce :","topTip":"Le chat IA peut aussi vous donner des liens directs vers des documents et des PDF téléchargeables. Demandez-lui simplement de créer ou de rechercher un document et il inclura des liens cliquables dans sa réponse.","manualIntro":"Si vous souhaitez comprendre pleinement le fonctionnement interne, ou préférez procéder manuellement, voici un cycle commercial complet étape par étape.","caTitle":"Analyses dynamiques – discutez pour obtenir n'importe quel graphique","caBadge":"Fonctionnalité phare","caBody":"Besoin d'un rapport que les modèles prédéfinis ne fournissent pas ? Décrivez-le simplement dans le chat. L'assistant appelle un modèle spécialiste du code qui écrit le rapport pour vous, l'exécute en direct sur vos données ERP et renvoie un lien partageable. Le graphique ou le tableau s'affiche en quelques secondes et est enregistré comme brouillon sous « Analyses personnalisées » dans la barre latérale – vous pouvez le rouvrir, partager l'URL avec vos collègues ou demander à l'assistant de l'affiner.","caTryAsking":"Essayez de demander (cliquez pour préremplir dans le chat) :","caPrompt1":"Montre-moi les 10 meilleurs clients par chiffre d'affaires cette année en diagramme à barres","caPrompt2":"Tendance mensuelle des achats par fournisseur sur tous les mois, étiquette l'axe des x avec le nom du fournisseur","caPrompt3":"Articles les plus vendus par quantité, avec un tableau et un graphique","caPrompt4":"Quels clients me doivent le plus en ce moment ?","caTryInChat":"Essayer dans le chat","caOpenWorkspace":"Ouvrir l'espace d'analyse","flowTitle":"Aperçu du flux des documents","salesCycle":"Cycle de vente","purchaseCycle":"Cycle d'achat","returns":"Retours","flowShortcut":"Raccourci : le devis peut ignorer la commande client et aller directement à la facture de vente ou au bon de livraison","legendStock":"impact stock","legendGl":"impact comptable","legendCash":"impact trésorerie","legendReversal":"annulation","lifecycleTitle":"Cycle de vie du document","lcDraftLabel":"Brouillon :","lcDraftDesc":"Modifiable. Aucun impact financier ni sur le stock.","lcSubmittedLabel":"Validé :","lcSubmittedDesc":"Verrouillé. Les écritures comptables et de stock sont générées.","lcCancelledLabel":"Annulé :","lcCancelledDesc":"Toutes les écritures sont inversées. Le document est archivé définitivement.","lcNoDelete":"Il n'y a pas de suppression. Pour annuler un brouillon : validez-le, puis annulez-le.","lcCorrect":"Pour corriger un document validé : annulez-le et créez-en un nouveau.","readyTitle":"Vous êtes prêt","readyBody1":"Vous comprenez maintenant le cycle complet. Un scénario réel typique ressemble à ceci : vous recevez une commande client (Devis → Commande client), vérifiez le stock, achetez ce dont vous avez besoin (Bon de commande → Bon de réception → Facture d'achat → Paiement), livrez au client (Bon de livraison), facturez-le (Facture de vente) et encaissez le paiement (Paiement). Si quelque chose revient, créez un avoir et un retour de bon de livraison pour inverser les finances et le stock. La Balance comptable confirme que tout est équilibré.","readyBody2":"Vous pouvez aussi tout faire via le chat IA en conversation naturelle – décrivez simplement ce dont vous avez besoin et il créera les bons documents pour vous.","chips":{"Quotation":"Devis","Sales Order":"Commande client","Delivery Note":"Bon de livraison","Sales Invoice":"Facture de vente","Payment Entry":"Paiement","Purchase Order":"Bon de commande","Purchase Receipt":"Bon de réception","Purchase Invoice":"Facture d'achat","Credit Note":"Avoir","DN Return":"Retour BL","Debit Note":"Note de débit","Draft":"Brouillon","Submitted":"Validé","Cancelled":"Annulé"},"links":{"Go to Setup":"Aller à la configuration","Customers":"Clients","Suppliers":"Fournisseurs","Items":"Articles","Warehouses":"Entrepôts","New Quotation":"Nouveau devis","View Quotations":"Voir les devis","Stock Balance":"Solde de stock","New Purchase Order":"Nouveau bon de commande","Purchase Receipts":"Bons de réception","Purchase Invoices":"Factures d'achat","Delivery Notes":"Bons de livraison","Sales Invoices":"Factures de vente","New Payment Entry":"Nouveau paiement","New Stock Entry":"Nouveau mouvement de stock","New Journal Entry":"Nouvelle écriture de journal","Profit & Loss":"Compte de résultat","Balance Sheet":"Bilan","AR Aging":"Balance âgée clients","AP Aging":"Balance âgée fournisseurs","Trial Balance":"Balance comptable","General Ledger":"Grand livre","Open AI Chat":"Ouvrir le chat IA"},"steps":{"1":{"title":"Configurer votre société","description":"Créez une société avec une devise de base. Cela génère votre plan comptable (30 comptes standard répartis entre Actif, Passif, Capitaux propres, Produits et Charges) ainsi qu'un centre de coûts par défaut. Vous pouvez aussi charger des données de démonstration avec des clients, fournisseurs et articles d'exemple pour démarrer rapidement."},"2":{"title":"Créer les données de base","description":"Avant de pouvoir effectuer des transactions, vous avez besoin de fiches de base. Créez au moins un client, un fournisseur, un article (avec un prix standard) et un entrepôt. Si vous avez chargé les données de démonstration à l'étape 1, elles existent déjà.","tip":"Les articles ont un prix standard qui se remplit automatiquement lorsque vous les ajoutez à des documents. Vous pouvez toujours modifier le prix par transaction."},"3":{"title":"Créer un devis","description":"Un devis est une offre non contraignante adressée à un client. Sélectionnez un client, ajoutez des lignes avec quantités et prix, éventuellement des lignes de taxe, puis enregistrez. Les devis n'ont aucun impact financier – ils ne créent ni écritures comptables ni mouvements de stock. Définissez une date de validité pour que l'offre expire automatiquement."},"4":{"title":"Valider et convertir en commande client","description":"Ouvrez votre devis enregistré et cliquez sur Valider pour le confirmer. Cliquez ensuite sur « Créer une commande client » pour le convertir. La commande client représente un engagement confirmé du client. Elle n'a toujours aucun impact financier, mais réserve du stock à des fins de planification.","tip":"Seuls les documents validés peuvent être convertis à l'étape suivante. Brouillon → Valider → Convertir est le flux standard. Pour les affaires rapides, vous pouvez ignorer la commande client et passer directement du devis à la facture de vente ou au bon de livraison."},"5":{"title":"Vérifier le stock avant l'exécution","description":"Avant de pouvoir livrer, vérifiez si vous avez réellement l'article en stock. Ouvrez le rapport Solde de stock et recherchez l'article. Si votre entrepôt affiche une quantité nulle, vous devrez d'abord approvisionner le stock – soit par un bon de commande (achat auprès d'un fournisseur), soit par un mouvement de stock (réception manuelle).","tip":"C'est un scénario courant dans la pratique : vous vendez quelque chose, puis réalisez que vous devez d'abord l'acheter. L'ERP gère les deux flux."},"6":{"title":"Cycle d'achat : acheter du stock à un fournisseur","description":"Si vous devez acheter du stock, vous avez deux chemins valides. Le chemin standard est Bon de commande → Bon de réception → Facture d'achat : utilisez-le lorsque les marchandises arrivent avant ou séparément de la facture du fournisseur. Si la facture et la réception arrivent ensemble, vous pouvez créer directement la facture d'achat et activer « Mettre à jour le stock » afin que le même document réceptionne le stock et enregistre la dette fournisseur.","tip":"Utilisez d'abord le bon de réception lorsque vous recevez les marchandises séparément. Utilisez la facture d'achat avec « Mettre à jour le stock » lorsqu'une seule étape doit à la fois réceptionner le stock et comptabiliser la facture fournisseur. Dans ce chemin direct, indiquez un entrepôt sur chaque ligne d'article en stock."},"7":{"title":"Créer un bon de livraison","description":"Maintenant que vous avez du stock, revenez à votre commande client validée et cliquez sur « Créer un bon de livraison ». Indiquez l'entrepôt sur chaque ligne d'article (d'où partent les marchandises), puis validez. Cela sort le stock de l'entrepôt – votre solde de stock diminue.","tip":"Le bon de livraison est le document d'expédition. Il réduit le stock mais ne crée pas de facture. Vous pouvez livrer et facturer séparément."},"8":{"title":"Créer et valider la facture de vente","description":"À partir de la commande client validée, créez une facture de vente. Lorsque vous validez la facture, des écritures comptables sont générées : les Clients (créances) sont débités (le client vous doit de l'argent) et les Produits des ventes crédités (revenu acquis). Si des taxes sont configurées, la TVA collectée est également créditée. Le montant dû indique ce que le client doit encore."},"9":{"title":"Enregistrer les paiements clients","description":"Créez un paiement pour enregistrer l'argent reçu du client. Définissez le type de paiement sur « Receive » (Recevoir), sélectionnez le client, indiquez le montant et affectez-le à la facture de vente. Vous pouvez effectuer des paiements partiels – le montant dû de la facture se met à jour en conséquence. Créez des paiements supplémentaires jusqu'à ce que la facture soit entièrement réglée.","tip":"Les paiements partiels sont courants. Une facture de 10 000 peut être payée 3 000 maintenant et 7 000 plus tard. Chaque paiement réduit le montant dû."},"10":{"title":"Mouvements de stock (inventaire manuel)","description":"Utilisez les mouvements de stock pour les mouvements d'inventaire non liés aux achats ou aux ventes. « Material Receipt » ajoute du stock (soldes d'ouverture, ajustements). « Material Issue » sort du stock (pertes, consommation interne). « Material Transfer » déplace du stock entre entrepôts. Chaque mouvement met à jour le livre de stock avec une valorisation au coût moyen pondéré.","tip":"Pour les marchandises achetées, utilisez des bons de réception plutôt que des mouvements de stock – ils sont liés au bon de commande et offrent une piste d'audit appropriée."},"11":{"title":"Écritures de journal","description":"Les écritures de journal sont des ajustements comptables manuels – charges à payer, corrections, reclassements, soldes d'ouverture. Chaque écriture doit avoir des débits et crédits équilibrés (total débit = total crédit). Utilisez-les lorsqu'aucun autre type de document ne convient."},"12":{"title":"Paiements des salaires","description":"Lambda ERP gère les salaires à l'aide des outils comptables existants. Comptabilisez d'abord la charge salariale : créez une écriture de journal qui débite la Charge salariale et crédite les Salaires à payer pour le montant total de la paie. Payez ensuite les employés : créez une autre écriture de journal (ou un paiement) qui débite les Salaires à payer et crédite votre compte bancaire. Ce processus en deux étapes garde vos comptes exacts – la charge est enregistrée dans la bonne période et la sortie de trésorerie est suivie séparément.","tip":"Vous pouvez demander à l'assistant IA de le faire pour vous : « Comptabilise 15 000 de salaires pour avril » suivi de « Paie les salaires d'avril depuis la banque ». Il créera automatiquement les bonnes écritures de journal."},"13":{"title":"Retours et avoirs","description":"Lorsqu'un client retourne des marchandises ou que vous devez émettre un avoir, créez un retour. Les retours utilisent le même type de document avec des quantités négatives. Ouvrez une facture de vente validée et créez un avoir (retour de facture de vente) – cela inverse les écritures comptables et réduit le montant dû de la facture d'origine. Pour le stock, créez un retour de bon de livraison pour réintégrer les marchandises en entrepôt. Côté achats, créez une note de débit (retour de facture d'achat) pour inverser une facture fournisseur, ou un retour de bon de réception pour renvoyer des marchandises.","tip":"Un avoir est simplement une facture de vente avec is_return=1 et des quantités négatives. La même logique comptable s'applique – les montants négatifs basculent automatiquement du bon côté débit/crédit. Pour un retour de vente complet, vous avez besoin à la fois d'un avoir (financier) et d'un retour de bon de livraison (stock)."},"14":{"title":"Exécuter les rapports","description":"Vérifiez vos comptes. Le Compte de résultat montre les produits face aux charges et le résultat net d'une période. Le Bilan montre votre situation financière (actif = passif + capitaux propres). La Balance âgée clients montre qui vous doit de l'argent et le retard. La Balance âgée fournisseurs montre ce que vous devez aux fournisseurs. La Balance comptable vérifie l'intégrité de la partie double. Le Grand livre montre chaque écriture individuelle. Le Solde de stock montre l'inventaire actuel."},"15":{"title":"Travailler en devises étrangères","description":"Vos comptes sont tenus dans une seule devise de base (choisie à la configuration de la société), mais vous pouvez effectuer des transactions dans n'importe quelle devise. Définissez une devise sur une facture ou un effet – ou attribuez une devise par défaut à un client ou un fournisseur – et le taux de change de cette date est recherché automatiquement et enregistré sur le document. Le document conserve ses montants dans sa propre devise, tandis que le Grand livre comptabilise toujours dans votre devise de base. Lorsque vous encaissez ou payez ultérieurement à un taux différent, le gain ou la perte de change réalisé est comptabilisé automatiquement sur un compte Gain/Perte de change. Vous pouvez même détenir un solde bancaire en devise étrangère et le convertir plus tard au taux de votre banque – l'écart par rapport à sa valeur comptable est alors réalisé. En fin de mois, vous pouvez réévaluer les soldes en devises ouverts au taux de clôture (un gain/perte latent qui s'inverse à la période suivante), et vous pouvez consulter n'importe quel état financier converti dans une autre devise pour l'affichage.","tip":"Le plus simple est de passer par le chat IA – essayez « Crée une facture de vente pour Lumiere Audio en EUR », « Montre-moi le bilan en EUR » ou « Quelle est notre exposition de change latente en fin de mois ? ». Les données de démonstration incluent déjà un client EUR (Lumiere Audio SARL) dont la facture a été encaissée à un taux différent, ainsi qu'une facture fournisseur EUR ouverte – ouvrez le Grand livre pour voir les écritures de change réalisées."}},"setupTitle":"Configurez votre entreprise — guidé par l'IA","setupBadge":"Commencez ici","setupBody":"Dites à l'assistant quel type d'entreprise vous dirigez, et il construit votre plan comptable pour vous — adapté à votre secteur, avec les bons comptes et des valeurs par défaut cohérentes déjà en place. Il explique chaque décision et ne crée quoi que ce soit qu'après votre approbation.","setupSectors":"Adapté aux services, au commerce de détail & POS, à l'hôtellerie-restauration, au commerce de gros & distribution, à l'import/export, à la fabrication et à la construction.","setupGetStarted":"Commencer dans le chat","setupManual":"Vous préférez un formulaire manuel ? Utilisez la configuration classique.","setupManualLink":"Configuration classique","setupPrompt":"Je souhaite configurer mon entreprise. Pouvez-vous me guider ?"}`), tc = { title: "Aide au formatage", intro: "Le texte des Notes / Conditions prend en charge une mise en forme légère sur le PDF :", heading: "titre en gras", emphasis: "italique / gras", rule: "ligne de séparation horizontale", price: "prix aligné à droite à côté du texte ci-dessus (p. ex. Mensuel | CHF 380.—)", block: "Séparez les blocs par une ligne vide." }, nc = { period: "Période", allTime: "Toute la période", semantics: "Comptes de bilan : solde à la fin de la période. Comptes de résultat : mouvement de la période. Cliquez sur un compte pour son grand livre.", empty: "Aucun compte trouvé. Terminez d'abord la configuration de l'entreprise.", disabled: "désactivé", rootType: { Asset: "Actifs", Liability: "Passifs", Equity: "Capitaux propres", Income: "Produits", Expense: "Charges" } }, ac = { fleetCalendar: "Calendrier de flotte", yard: "Dépôt", machineType: "Type de machine", prev: "Préc.", today: "Aujourd'hui", next: "Suiv.", weeks: "semaines", newBooking: "Nouvelle réservation", reserved: "Réservé", onHire: "En location", machine: "Machine", loadError: "Impossible de charger le calendrier de flotte.", noAssets: "Aucun équipement à afficher. Ajoutez des machines sous Locations → Flotte.", pooledNote: "{{count}} réservation(s) en attente d’attribution", unassigned: "Pool — non attribué", poolWarning: "La capacité est réservée. Aucune machine précise n’est encore attribuée ; attribuez une machine avant la sortie." }, rc = { Unit: "Machine spécifique", Pool: "Pool – machine à affecter", Available: "Disponible", "On Hire": "En location", Out: "Sorti", Maintenance: "Maintenance", Retired: "Retiré", Reserved: "Réservé", Returned: "Retourné", Cancelled: "Annulé", Customer: "Client", Supplier: "Fournisseur" }, ic = { asset: { item_code: "L'article est le TYPE de machine et porte le tarif ; un équipement en est une unité physique. L'article doit avoir is_asset_tracked = 1.", asset_tag: "Plaque ou numéro de série — doit être unique sur tous les équipements.", status: "État actuel de l'unité (pas une période). Les unités retirées quittent le pool.", meter_reading: "Heures de fonctionnement / km." }, reservation: { item_code: "Réservation de pool ('une pelle de 1,7 t') : renseigner Article + Dépôt + Qté. Pour une machine précise, renseigner Équipement.", asset: "Réserver une machine précise. Une réservation d'unité consomme aussi une place du pool Article+Dépôt.", from_datetime: "Début exact — date et heure (p. ex. 14:27). Une date seule signifie 00:00.", to_datetime: "Fin exacte. Les fenêtres sont semi-ouvertes [de, à) : une location finissant à 09:00 et la suivante commençant à 09:00 ne se chevauchent PAS." } }, sc = { label: "Taxe de vente par défaut", none: "Aucune application automatique", hint: "Appliquée aux nouveaux devis, commandes et factures si les taxes ne sont pas explicitement définies. Les documents existants restent inchangés. Cliquez sur Enregistrer pour appliquer.", saveFirst: "Enregistrez d’abord l’entreprise, puis sélectionnez un de ses modèles de taxes de vente.", loadError: "Impossible de charger les modèles de taxes. La sélection enregistrée est conservée.", retry: "Réessayer", unavailable: "Le modèle enregistré n’est pas disponible comme modèle de taxes de vente pour cette entreprise. Vérifiez la sélection.", empty: "Aucun modèle de taxes de vente n’est encore défini pour cette entreprise." }, oc = { title: "Connecter une application", subtitle: "Choisissez où utiliser Lambda ERP. Nous afficherons la configuration adaptée.", other: "Autre application", customName: "Intégration personnalisée", signIn: "Se connecter", apiKey: "Clé API", access: "Accès", role_viewer: "Consulter les données", role_manager: "Créer et modifier", role_admin: "Accès administratif complet", roleHelp: "L’accès est toujours limité par vos propres droits ERP. Accordez uniquement ce dont cette application a besoin.", copied: "Copié", copyFailed: "La copie automatique a échoué. Sélectionnez le texte et copiez-le manuellement.", retry: "Réessayer", interface: "Type de connexion", chatDisabled: "L’accès à l’API de chat est désactivé. Demandez à un administrateur de l’activer dans les paramètres API ci-dessus.", restDisabled: "L’accès REST API / MCP est désactivé. Un administrateur doit l’activer dans les paramètres API avant de connecter cette application.", chatgptStep: "Ouvrez chatgpt.com dans votre navigateur. Sélectionnez « Plugins » dans la barre latérale gauche, cliquez sur « Add » (+) en haut à droite, puis choisissez « Create MCP App ». Saisissez un nom, par exemple Lambda ERP, et une courte description.", chatgptDeveloperMode: "Si « Create MCP App » n’est pas disponible, vérifiez que « Developer mode » est activé dans « Settings » → « Security and login ». S’il est déjà activé, allez directement dans « Plugins ». Des autorisations de l’espace de travail peuvent aussi être nécessaires.", claudeStep: "Dans Claude Desktop, ouvrez « Settings » → « Connectors » puis choisissez « Add custom connector ».", urlStep: "Collez cette URL de serveur. Utilisez la connexion OAuth ; laissez les champs facultatifs Client ID et Client Secret vides.", approveStep: "Connectez l’application, identifiez-vous dans Lambda ERP et approuvez l’accès souhaité.", noKeyNeeded: "Aucune clé API ni en-tête personnalisé n’est nécessaire. Choisissez l’accès sur l’écran d’autorisation.", officialGuide: "Ouvrir le guide de configuration", copyUrl: "Copier l’URL du serveur", keyReady: "Votre clé de connexion est prête", copySetup: "Copier la configuration", copyHeader: "Copier l’en-tête d’autorisation", cliStep: "Exécutez cette commande dans votre terminal, puis vérifiez la connexion avec /mcp dans Claude Code.", codexStep: "Ajoutez cette entrée à votre configuration Codex, puis redémarrez Codex et vérifiez /mcp.", done: "Terminé — masquer la clé", existing: "Connexions existantes", refresh: "Actualiser", lastUsed: "Dernière utilisation", notUsed: "Pas encore utilisée", consentTitle: "Se connecter à Lambda ERP", expired: "Cette demande de connexion est absente ou expirée. Recommencez depuis votre application.", loginFirst: "Connectez-vous à votre compte ERP pour examiner la demande de cette application.", consentBody: "« {{app}} » demande l’accès à votre ERP. Choisissez l’accès que vous souhaitez autoriser.", returnTo: "Après autorisation, vous retournerez vers", revokeHelp: "Vous pouvez révoquer cette connexion à tout moment dans Paramètres → Accès API.", deny: "Annuler", allow: "Autoriser la connexion", explain_viewer: "L’application peut lire les données et rapports ERP et générer des PDF. Elle ne peut pas modifier les données métier.", explain_manager: "L’application peut aussi créer, modifier, valider et annuler des documents et mettre à jour les données de base.", explain_admin: "L’application peut utiliser tous les outils ERP disponibles, y compris les actions administratives et la suppression de données de base." }, lc = {
8178
8178
  proposal: Bl,
8179
8179
  common: Il,
8180
8180
  datePresets: El,
@@ -8182,8 +8182,8 @@ const Co = { incomplete: "Draft saved; PDF output requires Customer, Company and
8182
8182
  bankReconciliation: Ol,
8183
8183
  smartSearch: Fl,
8184
8184
  language: zl,
8185
- header: ql,
8186
- titles: Ml,
8185
+ header: Ml,
8186
+ titles: ql,
8187
8187
  doctypes: $l,
8188
8188
  masters: Vl,
8189
8189
  nav: Ul,
@@ -8298,7 +8298,7 @@ function mc() {
8298
8298
  ),
8299
8299
  /* @__PURE__ */ t("p", { className: "text-sm text-fg-muted", children: e(`connections.explain_${s || "viewer"}`) }),
8300
8300
  /* @__PURE__ */ r("p", { className: "flex items-start gap-2 text-xs text-fg-muted", children: [
8301
- /* @__PURE__ */ t(qa, { size: 16, className: "shrink-0" }),
8301
+ /* @__PURE__ */ t(Ma, { size: 16, className: "shrink-0" }),
8302
8302
  e("connections.revokeHelp")
8303
8303
  ] }),
8304
8304
  u.isError && /* @__PURE__ */ t("p", { role: "alert", className: "text-sm text-red-600", children: u.error.message }),
@@ -8537,7 +8537,7 @@ function yc() {
8537
8537
  return /* @__PURE__ */ r("div", { className: "space-y-6", children: [
8538
8538
  /* @__PURE__ */ t("p", { className: "text-sm text-gray-500", children: "Import balances from your previous system. Each section creates the appropriate documents (Journal Entry, Stock Entry, or Invoices) and submits them automatically." }),
8539
8539
  /* @__PURE__ */ t(G, { title: "Common Settings", children: /* @__PURE__ */ r("div", { className: "flex flex-wrap items-end gap-4", children: [
8540
- /* @__PURE__ */ t(Me, { label: "Company", value: a, onChange: i, linkDoctype: "company", readOnly: !1 }),
8540
+ /* @__PURE__ */ t(qe, { label: "Company", value: a, onChange: i, linkDoctype: "company", readOnly: !1 }),
8541
8541
  /* @__PURE__ */ t(me, { label: "As-of Date", type: "date", value: o, onChange: (y) => s(y.target.value) })
8542
8542
  ] }) }),
8543
8543
  /* @__PURE__ */ r(G, { title: "1. Account Balances", children: [
@@ -8551,13 +8551,13 @@ function yc() {
8551
8551
  /* @__PURE__ */ t("th", { className: "px-2 py-2 text-right font-medium text-gray-500", children: "Credit" })
8552
8552
  ] }) }),
8553
8553
  /* @__PURE__ */ t("tbody", { children: p.map((y, R) => /* @__PURE__ */ r("tr", { children: [
8554
- /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t(Me, { value: y.account, onChange: (E) => h(p, u, R, "account", E), linkDoctype: "account", readOnly: !1, label: "" }) }),
8554
+ /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t(qe, { value: y.account, onChange: (E) => h(p, u, R, "account", E), linkDoctype: "account", readOnly: !1, label: "" }) }),
8555
8555
  /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ r("select", { value: y.party_type, onChange: (E) => h(p, u, R, "party_type", E.target.value), className: "w-full rounded border border-gray-300 px-2 py-1.5 text-sm", children: [
8556
8556
  /* @__PURE__ */ t("option", { value: "", children: "—" }),
8557
8557
  /* @__PURE__ */ t("option", { value: "Customer", children: "Customer" }),
8558
8558
  /* @__PURE__ */ t("option", { value: "Supplier", children: "Supplier" })
8559
8559
  ] }) }),
8560
- /* @__PURE__ */ t("td", { className: "px-2 py-1", children: y.party_type ? /* @__PURE__ */ t(Me, { label: "", value: y.party, onChange: (E) => h(p, u, R, "party", E), linkDoctype: y.party_type.toLowerCase(), readOnly: !1 }) : /* @__PURE__ */ t("span", { className: "text-gray-300 text-xs", children: "set party type first" }) }),
8560
+ /* @__PURE__ */ t("td", { className: "px-2 py-1", children: y.party_type ? /* @__PURE__ */ t(qe, { label: "", value: y.party, onChange: (E) => h(p, u, R, "party", E), linkDoctype: y.party_type.toLowerCase(), readOnly: !1 }) : /* @__PURE__ */ t("span", { className: "text-gray-300 text-xs", children: "set party type first" }) }),
8561
8561
  /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t("input", { type: "number", value: y.debit, onChange: (E) => h(p, u, R, "debit", E.target.value), className: "w-24 rounded border border-gray-300 px-2 py-1.5 text-right text-sm", placeholder: "0.00" }) }),
8562
8562
  /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t("input", { type: "number", value: y.credit, onChange: (E) => h(p, u, R, "credit", E.target.value), className: "w-24 rounded border border-gray-300 px-2 py-1.5 text-right text-sm", placeholder: "0.00" }) })
8563
8563
  ] }, R)) })
@@ -8574,7 +8574,7 @@ function yc() {
8574
8574
  ] }),
8575
8575
  /* @__PURE__ */ r(G, { title: "2. Stock Balances", children: [
8576
8576
  /* @__PURE__ */ t("p", { className: "mb-3 text-xs text-gray-500", children: "Enter your current inventory. Creates a Stock Entry (Opening Stock) — posts Dr Stock In Hand / Cr Opening Balance Equity so the P&L isn't distorted by day-one inventory." }),
8577
- /* @__PURE__ */ t("div", { className: "mb-3", children: /* @__PURE__ */ t(Me, { label: "Warehouse", value: l, onChange: c, linkDoctype: "warehouse", readOnly: !1 }) }),
8577
+ /* @__PURE__ */ t("div", { className: "mb-3", children: /* @__PURE__ */ t(qe, { label: "Warehouse", value: l, onChange: c, linkDoctype: "warehouse", readOnly: !1 }) }),
8578
8578
  /* @__PURE__ */ t("div", { children: /* @__PURE__ */ r("table", { className: "min-w-full text-sm", children: [
8579
8579
  /* @__PURE__ */ t("thead", { className: "bg-gray-50", children: /* @__PURE__ */ r("tr", { children: [
8580
8580
  /* @__PURE__ */ t("th", { className: "px-2 py-2 text-left font-medium text-gray-500", children: "Item" }),
@@ -8582,7 +8582,7 @@ function yc() {
8582
8582
  /* @__PURE__ */ t("th", { className: "px-2 py-2 text-right font-medium text-gray-500", children: "Rate" })
8583
8583
  ] }) }),
8584
8584
  /* @__PURE__ */ t("tbody", { children: f.map((y, R) => /* @__PURE__ */ r("tr", { children: [
8585
- /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t(Me, { label: "", value: y.item_code, onChange: (E) => h(f, m, R, "item_code", E), linkDoctype: "item", readOnly: !1 }) }),
8585
+ /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t(qe, { label: "", value: y.item_code, onChange: (E) => h(f, m, R, "item_code", E), linkDoctype: "item", readOnly: !1 }) }),
8586
8586
  /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t("input", { type: "number", value: y.qty, onChange: (E) => h(f, m, R, "qty", E.target.value), className: "w-24 rounded border border-gray-300 px-2 py-1.5 text-right text-sm", placeholder: "0" }) }),
8587
8587
  /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t("input", { type: "number", value: y.rate, onChange: (E) => h(f, m, R, "rate", E.target.value), className: "w-24 rounded border border-gray-300 px-2 py-1.5 text-right text-sm", placeholder: "0.00" }) })
8588
8588
  ] }, R)) })
@@ -8616,7 +8616,7 @@ function yc() {
8616
8616
  /* @__PURE__ */ t("option", { value: "purchase", children: "Purchase (AP)" })
8617
8617
  ] }) }),
8618
8618
  /* @__PURE__ */ t("td", { className: "px-2 py-1", children: /* @__PURE__ */ t(
8619
- Me,
8619
+ qe,
8620
8620
  {
8621
8621
  label: "",
8622
8622
  value: y.party,
@@ -9225,8 +9225,8 @@ function xc() {
9225
9225
  type: "checkbox",
9226
9226
  checked: be,
9227
9227
  disabled: !z && !be,
9228
- onChange: (ne) => b((qe) => {
9229
- const Te = { ...qe };
9228
+ onChange: (ne) => b((Me) => {
9229
+ const Te = { ...Me };
9230
9230
  return ne.target.checked ? Te[re] = B.suggested_allocation : delete Te[re], Te;
9231
9231
  }),
9232
9232
  className: "h-4 w-4 rounded border-line text-brand"
@@ -9255,7 +9255,7 @@ function xc() {
9255
9255
  step: "0.01",
9256
9256
  disabled: !be,
9257
9257
  value: be ? x[re] : "",
9258
- onChange: (ne) => b((qe) => ({ ...qe, [re]: Number(ne.target.value) })),
9258
+ onChange: (ne) => b((Me) => ({ ...Me, [re]: Number(ne.target.value) })),
9259
9259
  "aria-label": e("bankReconciliation.allocation")
9260
9260
  }
9261
9261
  )
@@ -9470,7 +9470,7 @@ function Tt({ value: e, label: n }) {
9470
9470
  l(!0);
9471
9471
  }
9472
9472
  }, children: [
9473
- i ? /* @__PURE__ */ t(Ma, { size: 15 }) : /* @__PURE__ */ t(mi, { size: 15 }),
9473
+ i ? /* @__PURE__ */ t(qa, { size: 15 }) : /* @__PURE__ */ t(mi, { size: 15 }),
9474
9474
  i ? a("connections.copied") : n
9475
9475
  ] }),
9476
9476
  /* @__PURE__ */ t("span", { className: "text-xs text-fg-muted", role: "status", children: s ? a("connections.copyFailed") : "" })
@@ -9548,6 +9548,7 @@ http_headers = { Authorization = "Bearer ${m}" }`, y = ["viewer", "manager", "ad
9548
9548
  n("connections.approveStep")
9549
9549
  ] }),
9550
9550
  /* @__PURE__ */ t("p", { className: "text-xs text-fg-muted", children: n("connections.noKeyNeeded") }),
9551
+ i === "chatgpt" && /* @__PURE__ */ t("p", { className: "text-xs text-fg-muted", children: n("connections.chatgptDeveloperMode") }),
9551
9552
  /* @__PURE__ */ t(
9552
9553
  "a",
9553
9554
  {
@@ -9572,13 +9573,13 @@ http_headers = { Authorization = "Bearer ${m}" }`, y = ["viewer", "manager", "ad
9572
9573
  )
9573
9574
  ] }),
9574
9575
  !m && /* @__PURE__ */ r("p", { className: "flex items-start gap-2 text-xs text-fg-muted", children: [
9575
- /* @__PURE__ */ t(qa, { size: 16, className: "shrink-0" }),
9576
+ /* @__PURE__ */ t(Ma, { size: 16, className: "shrink-0" }),
9576
9577
  n("connections.roleHelp")
9577
9578
  ] }),
9578
9579
  g.isError && /* @__PURE__ */ t("p", { className: "text-sm text-red-600", role: "alert", children: g.error.message }),
9579
9580
  m && /* @__PURE__ */ r("div", { className: "space-y-4", role: "status", children: [
9580
9581
  /* @__PURE__ */ r("p", { className: "flex items-center gap-2 font-medium text-fg", children: [
9581
- /* @__PURE__ */ t(Ma, { size: 18, className: "text-green-600" }),
9582
+ /* @__PURE__ */ t(qa, { size: 18, className: "text-green-600" }),
9582
9583
  n("connections.keyReady")
9583
9584
  ] }),
9584
9585
  /* @__PURE__ */ t("p", { className: "text-xs text-fg-muted", children: n("settings.chatApiTokenOnce") }),
@@ -10352,7 +10353,7 @@ const Rc = [
10352
10353
  ], Bc = [
10353
10354
  { index: !0, element: /* @__PURE__ */ t(Tc, {}) },
10354
10355
  { path: "setup", element: /* @__PURE__ */ t(ro, {}) },
10355
- { path: "setup/opening-balances", element: /* @__PURE__ */ t(Mi, { children: /* @__PURE__ */ t(yc, {}) }) },
10356
+ { path: "setup/opening-balances", element: /* @__PURE__ */ t(qi, { children: /* @__PURE__ */ t(yc, {}) }) },
10356
10357
  { path: "accounting/bank-statements", element: /* @__PURE__ */ t(ea, { children: /* @__PURE__ */ t(vc, {}) }) },
10357
10358
  { path: "accounting/bank-reconciliation", element: /* @__PURE__ */ t(ea, { children: /* @__PURE__ */ t(xc, {}) }) },
10358
10359
  { path: "tutorial", element: /* @__PURE__ */ t(mo, {}) },
@@ -10404,7 +10405,7 @@ function Ec() {
10404
10405
  ...Pa(Rc, Dr),
10405
10406
  {
10406
10407
  path: "/",
10407
- element: /* @__PURE__ */ t(qi, { children: /* @__PURE__ */ t(Fi, {}) }),
10408
+ element: /* @__PURE__ */ t(Mi, { children: /* @__PURE__ */ t(Fi, {}) }),
10408
10409
  children: Pa(Bc, Pr)
10409
10410
  }
10410
10411
  ];
@@ -10426,7 +10427,7 @@ function Lc({ slug: e, parentSlug: n, linkField: a }) {
10426
10427
  }
10427
10428
  async function Oc() {
10428
10429
  try {
10429
- const e = await q(
10430
+ const e = await M(
10430
10431
  "/chat-doctypes",
10431
10432
  { signal: AbortSignal.timeout(1500) }
10432
10433
  );
@@ -10453,7 +10454,7 @@ async function id(e = {}) {
10453
10454
  throw new Error('bootstrap: no mount element (pass rootElement or add <div id="root">)');
10454
10455
  const a = e.queryClient ?? Fc();
10455
10456
  await Oc();
10456
- const i = Mr(Ec());
10457
+ const i = qr(Ec());
10457
10458
  Or(n).render(
10458
10459
  /* @__PURE__ */ t(Lr, { children: /* @__PURE__ */ t(Fr, { client: a, children: /* @__PURE__ */ t(ki, { children: /* @__PURE__ */ t($r, { router: i }) }) }) })
10459
10460
  );
@@ -10556,7 +10557,7 @@ export {
10556
10557
  Jc as registerNavGroup,
10557
10558
  Zc as registerNavItem,
10558
10559
  Ic as registerRoute,
10559
- q as request,
10560
+ M as request,
10560
10561
  Yc as setDateLocale,
10561
10562
  ir as setListContext,
10562
10563
  He as useAuth,