66 lines
4.8 KiB
JavaScript
66 lines
4.8 KiB
JavaScript
// Navigation and read models only. Financial values are copied from preserved sources.
|
|
export const billingSections = [
|
|
{id:'billing_all',label:'Todos os documentos'},
|
|
{id:'service_invoices',label:'Facturas'},
|
|
{id:'billing_invoice_receipts',label:'Facturas-recibo',pending:'A emissão de facturas-recibo ainda não está implementada. Uma factura e um recebimento não serão reunidos automaticamente neste tipo de documento.'},
|
|
{id:'billing_receipts',label:'Recibos',pending:'O documento fiscal Recibo ainda não está implementado. O percurso existente de recebimentos e afectações de teste conserva a sua identidade própria.'},
|
|
{id:'billing_credit_notes',label:'Notas de crédito',pending:'A emissão de notas de crédito ainda não está implementada. Pedidos de correcção existentes não são notas de crédito e não alteram automaticamente a dívida, o stock ou o dinheiro.'},
|
|
{id:'billing_quotes',label:'Orçamentos',pending:'A proposta comercial ao cliente e a sua conversão em rascunho ainda não estão implementadas. Este orçamento comercial é distinto do controlo orçamental de Compras e do orçamento de custos industriais.'},
|
|
{id:'billing_drafts',label:'Rascunhos'},
|
|
];
|
|
|
|
export function billingAccess(can,unit) {
|
|
return (unit!==null && (can('service_invoices.view',unit)||can('billing_drafts.view',unit)||can('service_receivables.view',unit)))
|
|
|| can('issuer_documents.view')||can('client_documents.view')||(unit!==null&&can('draft_documents.view',unit));
|
|
}
|
|
|
|
export function visibleBillingSections(can,unit) {
|
|
if(!billingAccess(can,unit))return [];
|
|
return billingSections.filter(s=>s.id==='service_invoices'?unit!==null&&can('service_invoices.view',unit)
|
|
:s.id==='billing_drafts'?unit!==null&&can('billing_drafts.view',unit):true);
|
|
}
|
|
|
|
export function invoiceRow(record,detail) {
|
|
const snapshot=detail.snapshot,doc=snapshot.document;
|
|
return {key:`invoice:${record.id}`,id:record.id,type:'invoice',typeLabel:'Factura de teste',number:`${record.series_code} / ${record.number}`,
|
|
client:doc.client?.name??null,nuit:doc.client?.data?.nuit??null,issuedDate:(snapshot.issued_local??record.issued_at)?.slice(0,10)??null,
|
|
dueDate:null,total:snapshot.calculation?.total_document_proposed??null,currency:doc.data?.currency??null,
|
|
series:record.series_code,author:null,state:'Emitido apenas em teste',balance:null,payment:'Indeterminado',detail};
|
|
}
|
|
|
|
export function draftRow(record) {
|
|
return {key:`draft:${record.id}`,id:record.id,type:'draft',typeLabel:'Rascunho',number:`Interno #${record.id}`,name:record.name,
|
|
client:record.data?.client_name??null,nuit:null,issuedDate:null,dueDate:null,total:null,base:record.base_amount??null,currency:record.data?.currency??null,
|
|
series:null,author:record.actor_id??null,state:record.workshop_commercial_state==='superseded'?'Histórico / substituído':record.workshop_commercial_state==='cancelled'?'Cancelado':record.internal_confirmation?'Confirmado internamente':'Rascunho',
|
|
balance:null,payment:'Não aplicável',version:record.version,updatedAt:record.updated_at};
|
|
}
|
|
|
|
export function filterBillingRows(rows,filters) {
|
|
const normalise=v=>String(v??'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').toLocaleLowerCase('pt');
|
|
const query=normalise(filters.search).trim();
|
|
return rows.filter(r=>(!query||[r.number,r.name,r.client,r.nuit].some(v=>normalise(v).includes(query)))
|
|
&&(!filters.type||r.type===filters.type)&&(!filters.currency||r.currency===filters.currency)
|
|
&&(!filters.series||r.series===filters.series)&&(!filters.state||r.state===filters.state)
|
|
&&(!filters.author||String(r.author??'')===filters.author)
|
|
&&(!filters.from||(r.issuedDate&&r.issuedDate>=filters.from))&&(!filters.to||(r.issuedDate&&r.issuedDate<=filters.to)));
|
|
}
|
|
|
|
export async function loadBillingRows(api,can,unit) {
|
|
const jobs=[];
|
|
if(unit!==null&&can('service_invoices.view',unit))jobs.push(['Facturas',async()=>{
|
|
const list=await api('/api/service-invoices');
|
|
if(!list.test_enabled)return {rows:[],notice:list.notice??'Documentos de teste indisponíveis neste ambiente.'};
|
|
const rows=[];
|
|
// Bound simultaneous requests while reusing the existing scoped immutable document endpoint.
|
|
for(let start=0;start<list.invoices.length;start+=6){
|
|
rows.push(...await Promise.all(list.invoices.slice(start,start+6).map(async r=>invoiceRow(r,await api(`/api/service-invoices/${r.id}`)))));
|
|
}
|
|
return {rows};
|
|
}]);
|
|
if(unit!==null&&can('billing_drafts.view',unit))jobs.push(['Rascunhos',async()=>({rows:(await api('/api/a/billing_drafts')).records.map(draftRow)})]);
|
|
const sources=await Promise.all(jobs.map(async([label,job])=>{
|
|
try{return {label,...await job()};}catch(e){return {label,error:e.message,rows:[]};}
|
|
}));
|
|
return {rows:sources.flatMap(s=>s.rows),sources};
|
|
}
|