fix: remove manual project creation, smart sequence release, received-invoices schema fix

- Remove ProjectCreate page, POST /projects endpoint, and next-number endpoint
- Projects can only be created through orders (shared numbering sequence)
- Remove dead CreateProjectSchema and createProject service function
- Delete 'order' row from number_sequences (unused; code uses 'shared')
- Smart sequence release: decrement last_number only when deleting the highest number
- Fix received-invoices stats referencing non-existent is_deleted and amount_czk columns
- Update deploy instructions in CLAUDE.md (npm install, prisma migrate deploy)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-04-28 11:36:08 +02:00
parent 3481b97d47
commit 82919d39f6
12 changed files with 86 additions and 558 deletions

View File

@@ -110,13 +110,51 @@ async function previewNextSequence(
/**
* Release a sequence number back to the pool.
* NOTE: Blindly decrementing can cause duplicate numbers if numbers were
* allocated after this one. Sequence numbers are consumed but not returned
* to the pool — this is intentionally a no-op.
* Only decrements if the deleted entity held the current highest number,
* preventing gaps while avoiding duplicate re-allocation.
*/
async function releaseSequence(_type: string, _year: number) {
// No-op: decrementing can cause duplicate sequence numbers.
// Sequence numbers are consumed but never returned to the pool.
async function releaseSequence(
type: string,
year: number,
deletedNumber?: string,
) {
if (!deletedNumber) return;
const existing = await prisma.$queryRaw<Array<{ last_number: number }>>`
SELECT last_number FROM number_sequences
WHERE \`type\` = ${type} AND \`year\` = ${year}
`;
if (existing.length === 0) return;
const settings = await getSettings();
const pattern =
type === "shared"
? settings?.order_number_pattern || DEFAULT_ORDER_PATTERN
: type === "invoice"
? settings?.invoice_number_pattern || DEFAULT_INVOICE_PATTERN
: settings?.offer_number_pattern || DEFAULT_OFFER_PATTERN;
const code =
type === "shared"
? settings?.order_type_code || "71"
: type === "invoice"
? settings?.invoice_type_code || "81"
: "";
const prefix = type === "offer" ? settings?.quotation_prefix || "NA" : "";
const highestNumber = applyPattern(pattern, {
year,
prefix,
code,
seq: existing[0].last_number,
});
if (deletedNumber === highestNumber && existing[0].last_number > 0) {
await prisma.$executeRaw`
UPDATE number_sequences
SET \`last_number\` = ${existing[0].last_number - 1}
WHERE \`type\` = ${type} AND \`year\` = ${year}
`;
}
}
/** Verify a shared number is not already used by an order or project. */
@@ -265,19 +303,40 @@ export async function previewInvoiceNumber(
return { number, next_number: number };
}
/** Release an offer number back to the pool (decrement sequence). */
export async function releaseOfferNumber(year?: number) {
await releaseSequence("offer", year || new Date().getFullYear());
/** Release an offer number back to the pool (decrement if highest). */
export async function releaseOfferNumber(
year?: number,
deletedNumber?: string,
) {
await releaseSequence(
"offer",
year || new Date().getFullYear(),
deletedNumber,
);
}
/** Release a shared number back to the pool (decrement sequence). */
export async function releaseSharedNumber(year?: number) {
await releaseSequence("shared", year || new Date().getFullYear());
/** Release a shared number back to the pool (decrement if highest). */
export async function releaseSharedNumber(
year?: number,
deletedNumber?: string,
) {
await releaseSequence(
"shared",
year || new Date().getFullYear(),
deletedNumber,
);
}
/** Release an invoice number back to the pool (decrement sequence). */
export async function releaseInvoiceNumber(year?: number) {
await releaseSequence("invoice", year || new Date().getFullYear());
/** Release an invoice number back to the pool (decrement if highest). */
export async function releaseInvoiceNumber(
year?: number,
deletedNumber?: string,
) {
await releaseSequence(
"invoice",
year || new Date().getFullYear(),
deletedNumber,
);
}
/** Preview what a pattern would produce (for settings UI) */