Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/publish-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

env:
REGISTRY: ghcr.io
IMAGE_PREFIX: ghcr.io/arrhes/fec
IMAGE_PREFIX: ghcr.io/comptasse/fec

jobs:
build-and-push:
Expand Down Expand Up @@ -55,7 +55,7 @@ jobs:
VERSION: ${{ steps.version.outputs.version }}
run: |
SRC="fec:${VERSION}"
DEST="${IMAGE_PREFIX}/website"
DEST="${IMAGE_PREFIX}"

docker tag "${SRC}" "${DEST}:${VERSION}"
docker push "${DEST}:${VERSION}"
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -629,8 +629,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

Arrhes - Open source double-entry accounting for French associations and SMEs.
Copyright (C) 2025 Arrhes
Comptasse - Open source double-entry accounting for French associations and SMEs.
Copyright (C) 2025 Comptasse

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# FEC

[![Licence AGPL-3.0](https://img.shields.io/badge/licence-AGPL--3.0-blue.svg)](LICENSE)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)
[![Node.js](https://img.shields.io/badge/Node.js-22-green.svg)](https://nodejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-7.0-blue.svg)](https://www.typescriptlang.org/)
[![Node.js](https://img.shields.io/badge/Node.js-24-green.svg)](https://nodejs.org/)

Outil open source de validation de **Fichiers des 脡critures Comptables** (FEC), conforme 脿 l'[article A47 A-1 du Livre des proc茅dures fiscales](https://www.legifrance.gouv.fr/codes/article_lc/LEGIARTI000027804775/).

Expand Down Expand Up @@ -52,28 +52,28 @@ Monorepo pnpm avec deux packages :
```
packages/
engine/ Moteur de validation FEC (TypeScript pur, aucune d茅pendance navigateur)
website/ Interface web (React 19, Vite 7, PandaCSS, TanStack Router)
website/ Interface web (React 19, Vite 8, PandaCSS, TanStack Router)
```

### `@arrhes/fec-engine`
### `@comptasse/fec-engine`

Librairie TypeScript exportant les parseurs, validateurs et types FEC. Peut 锚tre utilis茅e ind茅pendamment du site web.

```ts
import { validateFecFile } from "@arrhes/fec-engine"
import { validateFecFile } from "@comptasse/fec-engine"

const result = validateFecFile(fileContent, "123456789FEC20240101.txt")
// result.summary -> { errors: 0, warnings: 2, total: 19, lines: 1042 }
// result.checks -> FecCheckResult[]
```

### `@arrhes/fec-website`
### `@comptasse/fec-website`

Application React avec validation c么t茅 client. Le fichier est lu via `File.text()` et pass茅 au moteur de validation directement dans le navigateur.

## Pr茅requis

- [Node.js](https://nodejs.org/) >= 22
- [Node.js](https://nodejs.org/) >= 24
- [pnpm](https://pnpm.io/) >= 10
- [Docker](https://docs.docker.com/get-docker/) et Docker Compose (pour le d茅veloppement conteneuris茅 et le build de production)
- [just](https://github.com/casey/just) (optionnel, raccourcis de commandes)
Expand All @@ -82,7 +82,7 @@ Application React avec validation c么t茅 client. Le fichier est lu via `File.tex

```bash
# Cloner le d茅p么t
git clone https://github.com/arrhes/fec.git
git clone https://github.com/comptasse/fec.git
cd fec

# Installer les d茅pendances
Expand Down Expand Up @@ -143,10 +143,10 @@ Variables d'environnement requises (voir `workflows/build/.env.example`) :

| Composant | Technologie |
|-----------|-------------|
| Langage | TypeScript 5.9 |
| Runtime | Node.js 22 |
| Langage | TypeScript 7.0 |
| Runtime | Node.js 24 |
| Frontend | React 19 |
| Bundler | Vite 7 |
| Bundler | Vite 8 |
| Routeur | TanStack Router |
| CSS | PandaCSS |
| Tests | Vitest |
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.2.0
v0.3.0
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"preset": "recommended",
"correctness": {
"noUnusedImports": "warn",
"useExhaustiveDependencies": "warn",
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@arrhes/fec",
"name": "@comptasse/fec",
"private": true,
"license": "AGPL-3.0-only",
"scripts": {
Expand All @@ -15,11 +15,11 @@
"test:e2e": "playwright test --config=tests/playwright.config.ts"
},
"devDependencies": {
"@biomejs/biome": "2.3.15",
"@playwright/test": "1.58.2",
"playwright": "1.58.2"
"@biomejs/biome": "2.5.14",
"@playwright/test": "1.63.0",
"playwright": "1.63.0"
},
"workspaces": [
"./packages/*"
]
}
}
14 changes: 7 additions & 7 deletions packages/engine/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@arrhes/fec-engine",
"name": "@comptasse/fec-engine",
"type": "module",
"private": true,
"scripts": {
Expand All @@ -9,14 +9,14 @@
"test:watch": "vitest"
},
"devDependencies": {
"@types/node": "24.10.1",
"jsdom": "26.1.0",
"typescript": "5.9.3",
"vitest": "4.0.18"
"@types/node": "26.6.1",
"jsdom": "30.1.0",
"typescript": "7.0.2",
"vitest": "5.0.1"
},
"dependencies": {
"nanoid": "5.1.6",
"valibot": "1.2.0"
"nanoid": "6.0.1",
"valibot": "1.5.0"
},
"exports": {
".": {
Expand Down
34 changes: 17 additions & 17 deletions packages/engine/src/fec/checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export function checkCompteNum(parsed: FecParsedFile): FecCheckResult[] {

for (let i = 0; i < parsed.entries.length; i++) {
const entry = parsed.entries[i]!
const value = entry["CompteNum"]
const value = entry.CompteNum
if (value && value.length >= 3) {
const first3 = value.substring(0, 3)
if (!/^\d{3}$/.test(first3)) {
Expand Down Expand Up @@ -267,8 +267,8 @@ export function checkDebitCredit(parsed: FecParsedFile): FecCheckResult[] {
const entry = parsed.entries[i]!

if (hasDebitCredit) {
const debit = entry["Debit"]?.trim() ?? ""
const credit = entry["Credit"]?.trim() ?? ""
const debit = entry.Debit?.trim() ?? ""
const credit = entry.Credit?.trim() ?? ""
if (debit === "" && credit === "") {
results.push({
id: "DEBIT_CREDIT",
Expand All @@ -280,8 +280,8 @@ export function checkDebitCredit(parsed: FecParsedFile): FecCheckResult[] {
}

if (hasMontantSens) {
const montant = entry["Montant"]?.trim() ?? ""
const sens = entry["Sens"]?.trim() ?? ""
const montant = entry.Montant?.trim() ?? ""
const sens = entry.Sens?.trim() ?? ""
if (montant === "" || sens === "") {
results.push({
id: "DEBIT_CREDIT",
Expand All @@ -307,7 +307,7 @@ export function checkSensValues(parsed: FecParsedFile): FecCheckResult[] {

for (let i = 0; i < parsed.entries.length; i++) {
const entry = parsed.entries[i]!
const value = entry["Sens"]
const value = entry.Sens
if (value && value.trim() !== "" && !validValues.has(value.trim())) {
results.push({
id: "SENS_VALUES",
Expand Down Expand Up @@ -341,7 +341,7 @@ export function checkChronologicalOrder(parsed: FecParsedFile): FecCheckResult[]

for (let i = 0; i < parsed.entries.length; i++) {
const entry = parsed.entries[i]!
const validDate = entry["ValidDate"] ?? ""
const validDate = entry.ValidDate ?? ""

if (validDate && previousDate && validDate < previousDate) {
results.push({
Expand Down Expand Up @@ -371,7 +371,7 @@ export function checkEcritureNumSequence(parsed: FecParsedFile): FecCheckResult[
const nums: string[] = []

for (const entry of parsed.entries) {
const num = entry["EcritureNum"] ?? ""
const num = entry.EcritureNum ?? ""
if (num && !seen.has(num)) {
seen.add(num)
nums.push(num)
Expand Down Expand Up @@ -408,8 +408,8 @@ export function checkOpeningEntries(parsed: FecParsedFile): FecCheckResult[] {
// Opening entries typically use journal code starting with "AN", "OD" or "RAN"
// or the EcritureLib contains "a-nouveau" / "a nouveau" / "report"
const first = parsed.entries[0]!
const journalCode = (first["JournalCode"] ?? "").toUpperCase()
const lib = (first["EcritureLib"] ?? "").toLowerCase()
const journalCode = (first.JournalCode ?? "").toUpperCase()
const lib = (first.EcritureLib ?? "").toLowerCase()

const isOpeningJournal =
journalCode === "AN" || journalCode === "RAN" || journalCode === "OD" || journalCode.startsWith("AN")
Expand Down Expand Up @@ -448,16 +448,16 @@ export function checkDebitCreditBalance(parsed: FecParsedFile): FecCheckResult[]

for (let i = 0; i < parsed.entries.length; i++) {
const entry = parsed.entries[i]!
const num = entry["EcritureNum"] ?? ""
const num = entry.EcritureNum ?? ""
if (!num) continue

if (!groups.has(num)) {
groups.set(num, { debit: 0, credit: 0, firstLine: i + 2 })
}
const group = groups.get(num)!

const debit = parseDecimal(entry["Debit"] ?? "")
const credit = parseDecimal(entry["Credit"] ?? "")
const debit = parseDecimal(entry.Debit ?? "")
const credit = parseDecimal(entry.Credit ?? "")
group.debit += debit
group.credit += credit
}
Expand Down Expand Up @@ -553,8 +553,8 @@ export function checkPieceDateCoherence(parsed: FecParsedFile): FecCheckResult[]

for (let i = 0; i < parsed.entries.length; i++) {
const entry = parsed.entries[i]!
const pieceDate = entry["PieceDate"] ?? ""
const ecritureDate = entry["EcritureDate"] ?? ""
const pieceDate = entry.PieceDate ?? ""
const ecritureDate = entry.EcritureDate ?? ""

if (
pieceDate &&
Expand Down Expand Up @@ -629,8 +629,8 @@ export function checkDebitCreditExclusive(parsed: FecParsedFile): FecCheckResult

for (let i = 0; i < parsed.entries.length; i++) {
const entry = parsed.entries[i]!
const debitStr = entry["Debit"]?.trim() ?? ""
const creditStr = entry["Credit"]?.trim() ?? ""
const debitStr = entry.Debit?.trim() ?? ""
const creditStr = entry.Credit?.trim() ?? ""

// Only check lines where both fields have valid numeric values
if (debitStr === "" || creditStr === "") continue
Expand Down
65 changes: 31 additions & 34 deletions packages/engine/src/fec/index.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,41 @@
export type {
FecCheckSeverity,
FecCheckResult,
FecValidationSummary,
FecValidationResult,
FecColumnDefinition,
FecEntry,
FecParsedFile,
FecParsedLineIssue,
} from "./types.js"

export { BIC_COLUMNS, BIC_COLUMN_NAMES } from "./types.js"

export { parseFlatFile } from "./parseFlatFile.js"
export { parseXmlFile } from "./parseXmlFile.js"
export { validateFecFile } from "./validate.js"

export {
checkFileName,
checkHeaderPresence,
checkColumnOrder,
checkChronologicalOrder,
checkColumnCount,
checkSeparator,
checkDateFormat,
checkNumericFormat,
checkColumnOrder,
checkCompteNum,
checkMandatoryFields,
checkDateFormat,
checkDateValidity,
checkDateYearRange,
checkDebitCredit,
checkSensValues,
checkChronologicalOrder,
checkEcritureNumSequence,
checkOpeningEntries,
checkDebitCreditBalance,
checkEncoding,
checkDebitCreditExclusive,
checkEcritureNumSequence,
checkEmptyFile,
checkDateValidity,
checkPieceDateCoherence,
checkFieldCountMismatch,
checkEmptyLines,
checkDebitCreditExclusive,
checkEncoding,
checkFieldCountMismatch,
checkFileName,
checkHeaderPresence,
checkMandatoryFields,
checkNumericDotSeparator,
checkNumericFormat,
checkNumericThousandsSeparator,
checkDateYearRange,
checkOpeningEntries,
checkPieceDateCoherence,
checkSensValues,
checkSeparator,
} from "./checks.js"
export { parseFlatFile } from "./parseFlatFile.js"
export { parseXmlFile } from "./parseXmlFile.js"
export type {
FecCheckResult,
FecCheckSeverity,
FecColumnDefinition,
FecEntry,
FecParsedFile,
FecParsedLineIssue,
FecValidationResult,
FecValidationSummary,
} from "./types.js"
export { BIC_COLUMN_NAMES, BIC_COLUMNS } from "./types.js"
export { validateFecFile } from "./validate.js"
2 changes: 1 addition & 1 deletion packages/engine/src/fec/parseFlatFile.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { FecEntry, FecParsedFile, FecParsedLineIssue } from "./types.js"

export function parseFlatFile(content: string, fileName: string): FecParsedFile {
export function parseFlatFile(content: string, _fileName: string): FecParsedFile {
// Split lines, handling both \r\n and \n
const lines = content.split(/\r?\n/)

Expand Down
2 changes: 1 addition & 1 deletion packages/engine/src/fec/parseXmlFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function convertMontantSens(montant: string, sens: string): { debit: string; cre
return { debit: "", credit: "" }
}

export function parseXmlFile(content: string, fileName: string): FecParsedFile {
export function parseXmlFile(content: string, _fileName: string): FecParsedFile {
const parser = new DOMParser()
const doc = parser.parseFromString(content, "application/xml")

Expand Down
Loading
Loading