feat: add clients nomenclator, order edit/delete/devalidate, invoice types, dashboard redesign
- New clients table with PF/PJ support, fiscal data (CUI, IBAN, eFactura fields) - Full CRUD API for clients with search, sync integration - Order lifecycle: edit header (DRAFT), devalidate (VALIDAT→DRAFT), delete order/invoice - Invoice types: FACTURA (B2B) vs BON_FISCAL (B2C) with different nr formats - OrderCreateView redesigned as multi-step flow (client→vehicle→details) - Autocomplete from catalog_norme/catalog_preturi in OrderLineForm - Dashboard now combines stats + full orders table with filter tabs and search - ClientPicker and VehiclePicker with inline creation capability - Frontend schema aligned with backend (missing columns causing sync errors) - Mobile responsive fixes for OrderDetailView buttons Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,13 +6,19 @@ import { useAuthStore } from './auth.js'
|
||||
export const useOrdersStore = defineStore('orders', () => {
|
||||
const auth = useAuthStore()
|
||||
|
||||
async function getAll(statusFilter = null) {
|
||||
let sql = `SELECT * FROM orders WHERE tenant_id = ? ORDER BY created_at DESC`
|
||||
async function getAll(statusFilter = null, search = '') {
|
||||
let sql = `SELECT * FROM orders WHERE tenant_id = ?`
|
||||
const params = [auth.tenantId]
|
||||
if (statusFilter) {
|
||||
sql = `SELECT * FROM orders WHERE tenant_id = ? AND status = ? ORDER BY created_at DESC`
|
||||
sql += ` AND status = ?`
|
||||
params.push(statusFilter)
|
||||
}
|
||||
if (search && search.length >= 2) {
|
||||
sql += ` AND (nr_auto LIKE ? OR client_nume LIKE ? OR nr_comanda LIKE ?)`
|
||||
const like = `%${search}%`
|
||||
params.push(like, like, like)
|
||||
}
|
||||
sql += ` ORDER BY created_at DESC`
|
||||
return execSQL(sql, params)
|
||||
}
|
||||
|
||||
@@ -40,13 +46,23 @@ export const useOrdersStore = defineStore('orders', () => {
|
||||
const now = new Date().toISOString()
|
||||
const nr = `CMD-${Date.now().toString(36).toUpperCase()}`
|
||||
|
||||
// Lookup client info for denormalized fields
|
||||
let clientNume = '', clientTelefon = ''
|
||||
if (data.client_id) {
|
||||
const [c] = await execSQL(`SELECT * FROM clients WHERE id = ?`, [data.client_id])
|
||||
if (c) {
|
||||
clientNume = c.tip_persoana === 'PJ' ? (c.denumire || '') : [c.nume, c.prenume].filter(Boolean).join(' ')
|
||||
clientTelefon = c.telefon || ''
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup vehicle info for denormalized fields
|
||||
let clientNume = '', clientTelefon = '', nrAuto = '', marcaDenumire = '', modelDenumire = ''
|
||||
let nrAuto = '', marcaDenumire = '', modelDenumire = ''
|
||||
if (data.vehicle_id) {
|
||||
const [v] = await execSQL(`SELECT * FROM vehicles WHERE id = ?`, [data.vehicle_id])
|
||||
if (v) {
|
||||
clientNume = v.client_nume || ''
|
||||
clientTelefon = v.client_telefon || ''
|
||||
if (!clientNume) clientNume = v.client_nume || ''
|
||||
if (!clientTelefon) clientTelefon = v.client_telefon || ''
|
||||
nrAuto = v.nr_inmatriculare || ''
|
||||
const [marca] = await execSQL(`SELECT denumire FROM catalog_marci WHERE id = ?`, [v.marca_id])
|
||||
const [model] = await execSQL(`SELECT denumire FROM catalog_modele WHERE id = ?`, [v.model_id])
|
||||
@@ -56,16 +72,18 @@ export const useOrdersStore = defineStore('orders', () => {
|
||||
}
|
||||
|
||||
await execSQL(
|
||||
`INSERT INTO orders (id, tenant_id, nr_comanda, data_comanda, vehicle_id, tip_deviz_id, status, km_intrare, observatii, client_nume, client_telefon, nr_auto, marca_denumire, model_denumire, created_at, updated_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
||||
[id, auth.tenantId, nr, now, data.vehicle_id || null, data.tip_deviz_id || null,
|
||||
`INSERT INTO orders (id, tenant_id, nr_comanda, data_comanda, vehicle_id, client_id, tip_deviz_id, status, km_intrare, observatii, client_nume, client_telefon, nr_auto, marca_denumire, model_denumire, created_at, updated_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
||||
[id, auth.tenantId, nr, now, data.vehicle_id || null, data.client_id || null,
|
||||
data.tip_deviz_id || null,
|
||||
'DRAFT', data.km_intrare || 0, data.observatii || '',
|
||||
clientNume, clientTelefon, nrAuto, marcaDenumire, modelDenumire, now, now]
|
||||
)
|
||||
notifyTableChanged('orders')
|
||||
await syncEngine.addToQueue('orders', id, 'INSERT', {
|
||||
id, tenant_id: auth.tenantId, nr_comanda: nr, data_comanda: now,
|
||||
vehicle_id: data.vehicle_id, tip_deviz_id: data.tip_deviz_id,
|
||||
vehicle_id: data.vehicle_id, client_id: data.client_id,
|
||||
tip_deviz_id: data.tip_deviz_id,
|
||||
status: 'DRAFT', km_intrare: data.km_intrare || 0, observatii: data.observatii || '',
|
||||
client_nume: clientNume, client_telefon: clientTelefon, nr_auto: nrAuto,
|
||||
marca_denumire: marcaDenumire, model_denumire: modelDenumire
|
||||
@@ -137,6 +155,49 @@ export const useOrdersStore = defineStore('orders', () => {
|
||||
})
|
||||
}
|
||||
|
||||
async function updateHeader(orderId, data) {
|
||||
const now = new Date().toISOString()
|
||||
|
||||
// Re-denormalize client and vehicle info if IDs changed
|
||||
let clientNume = data.client_nume, clientTelefon = data.client_telefon
|
||||
let nrAuto = data.nr_auto, marcaDenumire = data.marca_denumire, modelDenumire = data.model_denumire
|
||||
|
||||
if (data.client_id) {
|
||||
const [c] = await execSQL(`SELECT * FROM clients WHERE id = ?`, [data.client_id])
|
||||
if (c) {
|
||||
clientNume = c.tip_persoana === 'PJ' ? (c.denumire || '') : [c.nume, c.prenume].filter(Boolean).join(' ')
|
||||
clientTelefon = c.telefon || ''
|
||||
}
|
||||
}
|
||||
if (data.vehicle_id) {
|
||||
const [v] = await execSQL(`SELECT * FROM vehicles WHERE id = ?`, [data.vehicle_id])
|
||||
if (v) {
|
||||
nrAuto = v.nr_inmatriculare || ''
|
||||
const [marca] = await execSQL(`SELECT denumire FROM catalog_marci WHERE id = ?`, [v.marca_id])
|
||||
const [model] = await execSQL(`SELECT denumire FROM catalog_modele WHERE id = ?`, [v.model_id])
|
||||
marcaDenumire = marca?.denumire || ''
|
||||
modelDenumire = model?.denumire || ''
|
||||
}
|
||||
}
|
||||
|
||||
await execSQL(
|
||||
`UPDATE orders SET client_id=?, vehicle_id=?, tip_deviz_id=?, km_intrare=?, observatii=?,
|
||||
client_nume=?, client_telefon=?, nr_auto=?, marca_denumire=?, model_denumire=?, updated_at=?
|
||||
WHERE id=?`,
|
||||
[data.client_id || null, data.vehicle_id || null, data.tip_deviz_id || null,
|
||||
data.km_intrare || 0, data.observatii || '',
|
||||
clientNume || '', clientTelefon || '', nrAuto || '', marcaDenumire || '', modelDenumire || '',
|
||||
now, orderId]
|
||||
)
|
||||
notifyTableChanged('orders')
|
||||
await syncEngine.addToQueue('orders', orderId, 'UPDATE', {
|
||||
client_id: data.client_id, vehicle_id: data.vehicle_id, tip_deviz_id: data.tip_deviz_id,
|
||||
km_intrare: data.km_intrare, observatii: data.observatii,
|
||||
client_nume: clientNume, client_telefon: clientTelefon, nr_auto: nrAuto,
|
||||
marca_denumire: marcaDenumire, model_denumire: modelDenumire
|
||||
})
|
||||
}
|
||||
|
||||
async function validateOrder(orderId) {
|
||||
const now = new Date().toISOString()
|
||||
await execSQL(`UPDATE orders SET status='VALIDAT', updated_at=? WHERE id=?`, [now, orderId])
|
||||
@@ -144,6 +205,59 @@ export const useOrdersStore = defineStore('orders', () => {
|
||||
await syncEngine.addToQueue('orders', orderId, 'UPDATE', { status: 'VALIDAT' })
|
||||
}
|
||||
|
||||
async function devalidateOrder(orderId) {
|
||||
const now = new Date().toISOString()
|
||||
await execSQL(`UPDATE orders SET status='DRAFT', updated_at=? WHERE id=?`, [now, orderId])
|
||||
notifyTableChanged('orders')
|
||||
await syncEngine.addToQueue('orders', orderId, 'UPDATE', { status: 'DRAFT' })
|
||||
}
|
||||
|
||||
async function deleteOrder(orderId) {
|
||||
await execSQL(`DELETE FROM order_lines WHERE order_id = ?`, [orderId])
|
||||
await execSQL(`DELETE FROM orders WHERE id = ?`, [orderId])
|
||||
notifyTableChanged('order_lines')
|
||||
notifyTableChanged('orders')
|
||||
await syncEngine.addToQueue('orders', orderId, 'DELETE', {})
|
||||
}
|
||||
|
||||
async function deleteInvoice(invoiceId) {
|
||||
const [inv] = await execSQL(`SELECT * FROM invoices WHERE id = ?`, [invoiceId])
|
||||
if (!inv) return
|
||||
// Set order back to VALIDAT
|
||||
if (inv.order_id) {
|
||||
const now = new Date().toISOString()
|
||||
await execSQL(`UPDATE orders SET status='VALIDAT', updated_at=? WHERE id=?`, [now, inv.order_id])
|
||||
notifyTableChanged('orders')
|
||||
await syncEngine.addToQueue('orders', inv.order_id, 'UPDATE', { status: 'VALIDAT' })
|
||||
}
|
||||
await execSQL(`DELETE FROM invoices WHERE id = ?`, [invoiceId])
|
||||
notifyTableChanged('invoices')
|
||||
await syncEngine.addToQueue('invoices', invoiceId, 'DELETE', {})
|
||||
}
|
||||
|
||||
async function searchNorme(query) {
|
||||
if (!query || query.length < 2) return []
|
||||
const like = `%${query}%`
|
||||
return execSQL(
|
||||
`SELECT n.*, a.denumire as ansamblu_denumire
|
||||
FROM catalog_norme n
|
||||
LEFT JOIN catalog_ansamble a ON n.ansamblu_id = a.id
|
||||
WHERE n.tenant_id = ? AND (n.denumire LIKE ? OR n.cod LIKE ?)
|
||||
ORDER BY n.denumire LIMIT 10`,
|
||||
[auth.tenantId, like, like]
|
||||
)
|
||||
}
|
||||
|
||||
async function searchPreturi(query) {
|
||||
if (!query || query.length < 2) return []
|
||||
const like = `%${query}%`
|
||||
return execSQL(
|
||||
`SELECT * FROM catalog_preturi WHERE tenant_id = ? AND denumire LIKE ?
|
||||
ORDER BY denumire LIMIT 10`,
|
||||
[auth.tenantId, like]
|
||||
)
|
||||
}
|
||||
|
||||
async function getStats() {
|
||||
const [total] = await execSQL(
|
||||
`SELECT COUNT(*) as cnt FROM orders WHERE tenant_id = ?`, [auth.tenantId]
|
||||
@@ -154,20 +268,28 @@ export const useOrdersStore = defineStore('orders', () => {
|
||||
const [validat] = await execSQL(
|
||||
`SELECT COUNT(*) as cnt FROM orders WHERE tenant_id = ? AND status = 'VALIDAT'`, [auth.tenantId]
|
||||
)
|
||||
const [facturat] = await execSQL(
|
||||
`SELECT COUNT(*) as cnt FROM orders WHERE tenant_id = ? AND status = 'FACTURAT'`, [auth.tenantId]
|
||||
)
|
||||
const [totalVehicles] = await execSQL(
|
||||
`SELECT COUNT(*) as cnt FROM vehicles WHERE tenant_id = ?`, [auth.tenantId]
|
||||
)
|
||||
const [revenue] = await execSQL(
|
||||
`SELECT COALESCE(SUM(total_general), 0) as s FROM orders WHERE tenant_id = ? AND status = 'VALIDAT'`, [auth.tenantId]
|
||||
`SELECT COALESCE(SUM(total_general), 0) as s FROM orders WHERE tenant_id = ? AND status IN ('VALIDAT', 'FACTURAT')`, [auth.tenantId]
|
||||
)
|
||||
return {
|
||||
totalOrders: total?.cnt || 0,
|
||||
draftOrders: draft?.cnt || 0,
|
||||
validatedOrders: validat?.cnt || 0,
|
||||
facturatedOrders: facturat?.cnt || 0,
|
||||
totalVehicles: totalVehicles?.cnt || 0,
|
||||
totalRevenue: revenue?.s || 0,
|
||||
}
|
||||
}
|
||||
|
||||
return { getAll, getById, getLines, getRecentOrders, create, addLine, removeLine, validateOrder, getStats }
|
||||
return {
|
||||
getAll, getById, getLines, getRecentOrders, create, addLine, removeLine,
|
||||
updateHeader, validateOrder, devalidateOrder, deleteOrder, deleteInvoice,
|
||||
searchNorme, searchPreturi, getStats
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user