Files
Investidor-V2/resources/js/lib/iva-production-option.ts
T

123 lines
5.8 KiB
TypeScript

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.` : ''}`,
};
}