Skip to content
Merged
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
7 changes: 2 additions & 5 deletions .github/workflows/pr-build-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,5 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install just
uses: extractions/setup-just@v2

- name: Run CI build (lint + test + build)
run: just build
- name: Run CI build
run: ./scripts/ci/pr-build-check.sh
33 changes: 4 additions & 29 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 All @@ -22,40 +22,15 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4

- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" = "release" ]; then
echo "version=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
else
echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"
fi

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build images with docker compose
- name: Build and publish images
env:
VERSION: ${{ steps.version.outputs.version }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
VITE_WEBSITE_BASE_URL: ${{ vars.VITE_WEBSITE_BASE_URL }}
run: |
docker compose -f workflows/build/compose.yml build

- name: Tag and push images
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
SRC="fec:${VERSION}"
DEST="${IMAGE_PREFIX}/website"

docker tag "${SRC}" "${DEST}:${VERSION}"
docker push "${DEST}:${VERSION}"
run: ./scripts/ci/publish-packages.sh
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"
Loading
Loading