export type ProductionTaxOption = { mode: '' | 'annual' | 'startup'; declarationDate: string; startupDate: string; returnStatus: '' | 'none' | 'declared'; returnDeclarationDate: string; operationDate: string; origin?: { rowId: string; description: string; establishment: string }; }; export const productionOptionVersion = 'production-option/2'; // Civil Code 279(c), read with 296: the term ends at 24:00 on this date. // A missing anniversary day is clamped within the same month, never rolled to March. export function productionMinimumTermEnd(effective: string): string { const year = Number(effective.slice(0, 4)) + 5, month = Number(effective.slice(5, 7)), day = Number(effective.slice(8, 10)); const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); const lastDay = [ 31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, ][month - 1]; return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}-${String(Math.min(day, lastDay)).padStart(2, '0')}`; } export const productionReturnEffective = (declarationDate: string): string => String(Number(declarationDate.slice(0, 4)) + 1).padStart(4, '0') + '-01-01'; export const emptyProductionTaxOption = (): ProductionTaxOption => ({ mode: '', declarationDate: '', startupDate: '', returnStatus: '', returnDeclarationDate: '', operationDate: '', }); export function productionOptionCheck( option: ProductionTaxOption | undefined, period: string, ): { error?: string; summary?: string } { const valid = (value: unknown): value is string => typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value) && Number(value.slice(0, 4)) >= 1 && Number.isFinite(new Date(value + 'T00:00:00Z').valueOf()) && new Date(value + 'T00:00:00Z').toISOString().slice(0, 10) === value; if ( !option || !['annual', 'startup'].includes(option.mode) || !valid(option.declarationDate) || !valid(option.operationDate) ) return { error: 'Indique o tipo/data da declaração de opção e a data da operação; não se presume renúncia eficaz.', }; if (option.operationDate.slice(0, 7) !== period) return { error: 'A data da operação deve pertencer ao período desta linha; não repartir montantes automaticamente.', }; let effective = String(Number(option.declarationDate.slice(0, 4)) + 1).padStart( 4, '0', ) + '-01-01'; if (option.mode === 'startup') { if ( !valid(option.startupDate) || option.declarationDate > option.startupDate ) return { error: 'Na opção constante da declaração de início, indique a data de início e uma declaração que não lhe seja posterior.', }; effective = option.startupDate; } if (option.operationDate < effective) return { error: `Opção ainda sem efeitos nesta operação; início de efeitos ${effective}.`, }; if (!['none', 'declared'].includes(option.returnStatus)) return { error: 'Indique expressamente se existe declaração de regresso à isenção; informação desconhecida não significa ausência de declaração.', }; if (option.returnStatus === 'none' && option.returnDeclarationDate) return { error: 'Foi indicada ausência de declaração de regresso, mas existe uma data guardada; resolva a divergência explicitamente.', }; let end = ''; if (option.returnStatus === 'declared') { if (!valid(option.returnDeclarationDate)) return { error: 'Data da declaração de regresso à isenção em falta ou inválida; não presumir uma data.', }; const anniversary = productionMinimumTermEnd(effective); if (option.returnDeclarationDate < option.declarationDate) return { error: 'A declaração de regresso antecede a declaração de opção; resolva a divergência do histórico.', }; if (option.returnDeclarationDate > anniversary) return { error: `A declaração de regresso indicada é posterior ao termo às 24 horas de ${anniversary}; o CIVA, art.11(3), exige entrega antes de expirar o prazo. Não presumir efeitos para esta declaração tardia.`, }; end = productionReturnEffective(option.returnDeclarationDate); if (end <= anniversary) return { error: `O regresso não pode produzir efeitos antes de terminado o prazo mínimo, às 24 horas de ${anniversary}.`, }; if (option.operationDate >= end) return { error: `Opção já cessou na data desta operação; regresso à isenção com efeitos ${end}. Reveja o tratamento.`, }; } return { summary: `Art.11 (${productionOptionVersion}): declaração ${option.declarationDate} (${option.mode === 'startup' ? 'opção no início da actividade' : 'opção anual'}); efeitos ${effective}; termo mínimo às 24 horas de ${productionMinimumTermEnd(effective)} (Código Civil, arts.279.º,c), e296.º); operação ${option.operationDate}; ${end ? `declaração de regresso ${option.returnDeclarationDate}, efeitos ${end} (CIVA, art.11(3)); o termo do prazo não antecipa estes efeitos` : 'ausência de declaração de regresso expressamente indicada; tributação continua, sem caducidade automática ao fim de cinco anos'}.${option.origin ? ` Opção reutilizada de ${option.origin.description} (${option.origin.rowId}), estabelecimento ${option.origin.establishment}; escolha confirmada nesta linha.` : ''}`, }; }