216 lines
7.2 KiB
TypeScript
216 lines
7.2 KiB
TypeScript
export type AdvancePeriodDeclaration = {
|
|
version: 1;
|
|
mode: '' | 'annual_non_civil';
|
|
subjectNuit: string;
|
|
year: string;
|
|
currentFrom: string;
|
|
currentTo: string;
|
|
currentReference: string;
|
|
previousFrom: string;
|
|
previousTo: string;
|
|
previousReference: string;
|
|
ppcBasisKey: string;
|
|
pecBasisKey: string;
|
|
confirmed: '' | 'yes';
|
|
};
|
|
export type AdvanceFiscalPeriod = {
|
|
from?: string | null;
|
|
to?: string | null;
|
|
subjectNuit?: string;
|
|
ppcBasisKey?: string;
|
|
pecBasisKey?: string;
|
|
declaration?: AdvancePeriodDeclaration;
|
|
} | null;
|
|
export const advanceCalendarVersion = 'IRPC-annual-noncivil-calendar/1';
|
|
const iso = (s?: string | null): s is string =>
|
|
Boolean(
|
|
s &&
|
|
/^20\d{2}-\d{2}-\d{2}$/.test(s) &&
|
|
Number.isFinite(Date.parse(s)) &&
|
|
new Date(s).toISOString().slice(0, 10) === s,
|
|
);
|
|
const index = (s: string) =>
|
|
Number(s.slice(0, 4)) * 12 + Number(s.slice(5, 7)) - 1;
|
|
const month = (from: string, offset: number) =>
|
|
new Date(
|
|
Date.UTC(
|
|
Number(from.slice(0, 4)),
|
|
Number(from.slice(5, 7)) - 1 + offset,
|
|
1,
|
|
),
|
|
)
|
|
.toISOString()
|
|
.slice(0, 7);
|
|
const end = (s: string) =>
|
|
new Date(Date.UTC(Number(s.slice(0, 4)), Number(s.slice(5, 7)), 0))
|
|
.toISOString()
|
|
.slice(0, 10);
|
|
const previousDay = (s: string) =>
|
|
new Date(Date.parse(s) - 86400000).toISOString().slice(0, 10);
|
|
const annual = (from?: string | null, to?: string | null) =>
|
|
iso(from) &&
|
|
iso(to) &&
|
|
from.endsWith('-01') &&
|
|
to === end(to) &&
|
|
index(to) - index(from) === 11;
|
|
export function newAdvancePeriodDeclaration(
|
|
subjectNuit: string,
|
|
year: string,
|
|
period: AdvanceFiscalPeriod,
|
|
): AdvancePeriodDeclaration {
|
|
const from = period?.from ?? '',
|
|
to = period?.to ?? '';
|
|
return {
|
|
version: 1,
|
|
mode: 'annual_non_civil',
|
|
subjectNuit,
|
|
year,
|
|
currentFrom: from,
|
|
currentTo: to,
|
|
currentReference: '',
|
|
previousFrom: iso(from) ? month(from, -12) + '-01' : '',
|
|
previousTo: iso(from) ? previousDay(from) : '',
|
|
previousReference: '',
|
|
ppcBasisKey: '',
|
|
pecBasisKey: '',
|
|
confirmed: '',
|
|
};
|
|
}
|
|
export function validateAdvancePeriodDeclaration(d: AdvancePeriodDeclaration) {
|
|
if (
|
|
!d ||
|
|
d.version !== 1 ||
|
|
!['', 'annual_non_civil'].includes(d.mode) ||
|
|
!['', 'yes'].includes(d.confirmed) ||
|
|
[
|
|
'subjectNuit',
|
|
'year',
|
|
'currentFrom',
|
|
'currentTo',
|
|
'currentReference',
|
|
'previousFrom',
|
|
'previousTo',
|
|
'previousReference',
|
|
'ppcBasisKey',
|
|
'pecBasisKey',
|
|
].some(
|
|
(k) =>
|
|
typeof (d as unknown as Record<string, unknown>)[k] !==
|
|
'string',
|
|
)
|
|
)
|
|
throw Error(
|
|
'Correspondência de períodos não recuperável;original preservado.',
|
|
);
|
|
}
|
|
export function advanceCalendar(
|
|
period: AdvanceFiscalPeriod | undefined,
|
|
year: string,
|
|
subjectNuit?: string,
|
|
) {
|
|
const from = period?.from ?? '',
|
|
to = period?.to ?? '';
|
|
if (!/^20\d{2}$/.test(year))
|
|
return {
|
|
version: advanceCalendarVersion,
|
|
mode: 'unavailable' as const,
|
|
candidate: false,
|
|
pending: ['exercício das estimativas por confirmar'],
|
|
from,
|
|
to,
|
|
ppcMonths: [],
|
|
pecMonths: [],
|
|
previousFrom: '',
|
|
previousTo: '',
|
|
source: '',
|
|
};
|
|
const civil =
|
|
!period || (from === year + '-01-01' && to === year + '-12-31');
|
|
const candidate = annual(from, to) && to.slice(0, 4) === year && !civil;
|
|
let mode: 'civil' | 'annual_non_civil' | 'unavailable' = civil
|
|
? 'civil'
|
|
: 'unavailable';
|
|
let pending: string[] = [];
|
|
const d = period?.declaration;
|
|
if (!civil) {
|
|
if (d?.mode === 'annual_non_civil') {
|
|
validateAdvancePeriodDeclaration(d);
|
|
const issues: string[] = [];
|
|
if (!candidate)
|
|
issues.push(
|
|
'o período L6 deve conter doze meses civis completos com termo no exercício indicado',
|
|
);
|
|
if (
|
|
d.subjectNuit !== (subjectNuit ?? period?.subjectNuit) ||
|
|
d.year !== year ||
|
|
d.currentFrom !== from ||
|
|
d.currentTo !== to
|
|
)
|
|
issues.push(
|
|
'titular,exercício ou datas declaradas divergem de L6',
|
|
);
|
|
if (
|
|
!annual(d.previousFrom, d.previousTo) ||
|
|
!iso(from) ||
|
|
d.previousTo !== previousDay(from) ||
|
|
d.previousTo.slice(0, 4) !== String(Number(year) - 1)
|
|
)
|
|
issues.push(
|
|
'o período anterior deve ser anual completo e imediatamente anterior ao período L6',
|
|
);
|
|
if (
|
|
!d.currentReference.trim() ||
|
|
!d.previousReference.trim() ||
|
|
d.confirmed !== 'yes'
|
|
)
|
|
issues.push(
|
|
'faltam fontes e confirmação da correspondência dos dois períodos',
|
|
);
|
|
if (issues.length)
|
|
pending = [
|
|
'período anual não civil: ' +
|
|
issues.join('; ') +
|
|
'. Preparação conservada.',
|
|
];
|
|
else mode = 'annual_non_civil';
|
|
} else
|
|
pending = [
|
|
'o período L6 ' +
|
|
(from || 'por confirmar') +
|
|
' — ' +
|
|
(to || 'por confirmar') +
|
|
' não corresponde ao ano civil ' +
|
|
year +
|
|
'. Reconciliar o enquadramento antes de usar as estimativas ordinárias; a preparação fica conservada.',
|
|
];
|
|
}
|
|
const start = mode === 'civil' ? year + '-01-01' : from;
|
|
return {
|
|
version: advanceCalendarVersion,
|
|
mode,
|
|
candidate,
|
|
pending,
|
|
from: mode === 'civil' ? year + '-01-01' : from,
|
|
to: mode === 'civil' ? year + '-12-31' : to,
|
|
ppcMonths:
|
|
mode === 'unavailable' ? [] : [4, 6, 8].map((i) => month(start, i)),
|
|
pecMonths:
|
|
mode === 'unavailable' ? [] : [5, 7, 9].map((i) => month(start, i)),
|
|
previousFrom:
|
|
mode === 'annual_non_civil'
|
|
? d!.previousFrom
|
|
: String(Number(year) - 1) + '-01-01',
|
|
previousTo:
|
|
mode === 'annual_non_civil'
|
|
? d!.previousTo
|
|
: String(Number(year) - 1) + '-12-31',
|
|
source:
|
|
mode === 'annual_non_civil'
|
|
? 'CIRPC7(2),Lei34/2007,BR52,original SHA256cdbef875ad7ccc1517b12faf5de0e893a70ed2ed81e33913cd6dc4a674474e6e; RIRPC27(1)(a)/29(1),BR16/I16-04-2008,original SHA256fc373ff5637edf75ec6a9595dec8763a8503d4ee786e3b7d8f1c742eae9d80ee. Períodos anuais completos documentados;5.º/7.º/9.º mês PPC e6.º/8.º/10.º mês PEC. ' +
|
|
d!.currentReference +
|
|
'; ' +
|
|
d!.previousReference
|
|
: '',
|
|
};
|
|
}
|