By Module
- Architecture By Module / Accounting
- acc_base, acc1 et acc2 — séparation des responsabilités
- Modèle de données et cycle de vie d’un Team Module
- Configuration Accounting — guide des JSON
- Constantes, préférences et conditions By Module
- UI data-driven, pages, tables et prefabs By Module
- Custom Commands et édition des pages By Module
- Système de formules
- Création du Module Type Data
- Pipeline des Bills — architecture et ajout d’un nouveau type
- Inventaire et stocks
- Immobilisations et amortissement
- Contrats
- Régularisations de fin de période
- Answer Key — génération des bonnes réponses
- Chaîne comptable — journaux, ledger et états financiers
- Grading, reassemble et calcul des scores
- Résultats, évaluations et correction de module
- Procédure annuel pour updater les taxes (updated version 2026)
- Contextes Accounting — cycle, héritage et données entre périodes
- Accounting 1 vs Accounting 2 — différences réelles
- Fuzzy matching — logique de correspondance du grading Accounting
Architecture By Module / Accounting
Rôle
By Module est le framework où une activité est composée de modules configurables. Accounting (acc1 et acc2) est l'implémentation principale de ce framework.
flowchart LR
ACT[Activity] --> AM[ActModule]
AM --> MS[ModuleSettings]
AM --> TM[teamsModules]
TM --> MTD[TeamModuleTypeData]
MTD --> P[pagesData]
MTD --> DD[dynamicDropdownsData]
MTD --> B[bills]
MTD --> INV[inventory]
MTD --> IMM[immobilizations]
MTD --> C[contracts]
P --> UI[UI data-driven]
TM --> RES[results]
Frontières
| Responsabilité | Emplacement |
|---|---|
| Modèles Unity | Assets/Src/Game/Data/ByModule/ |
| Controllers Unity | Assets/Src/Game/Controllers/*ByModule* et Assets/Src/UI/SimByModule/Controllers/ |
| UI By Module | Assets/Src/UI/SimByModule/ |
| Static data partagée | Assets/GameSpecific/byModule/Resources/data_byModule/ |
| Données propres Acc1/Acc2 | Assets/GameSpecific/acc1|acc2/Resources/data_acc*/ |
| Moteur serveur | server_scripts/_utils/by_module/ |
| Base Accounting | server_scripts/acc_base/ |
| Wrappers game-specific | server_scripts/acc1|acc2/command/ |
Chargement Unity
StaticDataControllerByModule initialise StaticDataByModule, puis les classes Acc1/Acc2 spécialisent au besoin. Le runtime charge à la fois la static data byModule et la static data du GameUid concret.
Flux d'une modification étudiante
sequenceDiagram
participant UI as Page By Module
participant C as TeamByModuleController
participant B as BackEndBrainCloud
participant GS as acc1/acc2 custom_command
participant U as utils_custom_command_by_module
participant D as teamsModules
UI->>C: modification
C->>B: CustomCommand(commandUid, data)
B->>GS: game-specific Cloud Script
GS->>U: executeCommandByModule
U->>D: lire / modifier / sauvegarder
U-->>C: réponse + message RTT
C-->>UI: mise à jour
Pages spécialisées déjà existantes
Les pages Système de formules, Configuration Accounting — guide des JSON, Pipeline des Bills et Procédure annuelle pour updater les taxes restent les références spécialisées et ne sont pas dupliquées ici.
Ajouter un nouveau module Accounting — vue end-to-end
Cette checklist est volontairement transversale. Les pages spécialisées donnent le détail de chaque moteur.
- Static data : ajouter le module dans
modules.json/ settings associés et vérifierregions_settings. - Pages : déclarer les
module_pagesetpages_settings. - Bills : configurer les bills; si nouveau type, suivre la page protégée Pipeline des Bills.
- Module Type Data : vérifier que les données initiales générées contiennent tout ce dont le module a besoin.
- UI : vérifier que chaque page est rendue par un type/prefab existant ou ajouter le support manquant.
- Answer Key : ajouter le calcul de chaque nouvelle page évaluée.
- Grading : ajouter le correcteur symétrique et les pondérations.
- Results : vérifier que le module produit les scores et evaluationTeam attendus.
- Textes : ajouter titres, labels, erreurs et tutoriels éventuels.
- Tutoriel : si le module introduit un nouveau flow, ajouter les TutorialUIObject/ScenarioStep nécessaires.
- Tests : create module data → answer key → grade à 100 % → mauvaises réponses.
- Région : tester au moins une région où le module est visible et une où il ne l'est pas.
Test de cohérence global recommandé
Le meilleur smoke test serveur est de générer le Module Type Data avec un seed fixe, construire l'Answer Key, utiliser cette Answer Key comme décisions étudiant, puis lancer grading + reassemble. Le résultat doit être cohérent à 100 % sur les pages évaluées.
ACC1 vs ACC2
Pour les différences réelles entre les deux jeux — contexte métier, datasets, petite caisse, tutoriels, wrappers et assets — voir Accounting 1 vs Accounting 2 — différences réelles.
acc_base, acc1 et acc2 — séparation des responsabilités
Principe
La vraie logique Accounting partagée côté serveur vit dans acc_base. acc1 et acc2 sont des wrappers qui instancient la même base avec un GameUid différent et exposent les commandes sous le namespace propre au jeu.
Utils
classDiagram
class GameSpecificUtilsAccBase
class GameSpecificUtilsAcc1
class GameSpecificUtilsAcc2
GameSpecificUtilsAccBase <|-- GameSpecificUtilsAcc1
GameSpecificUtilsAccBase <|-- GameSpecificUtilsAcc2
GameSpecificUtilsAcc1() appelle GameSpecificUtilsAccBase.call(this, "acc1"); Acc2 fait la même chose avec "acc2". Les deux enregistrent ensuite leur instance dans utilsByGameUid et exposent utilsAccGameSpecific.
Ce qui appartient à acc_base
- constantes et préférences Accounting;
- mapping bill → buy/sell;
- partenaires et comptes impactés;
- seed de Team Module;
- amortissement d'immobilisations;
- calculs contrats/régularisations;
- settings régionaux;
- résolution act/module/page settings;
- comptes utilisés et structure de pages;
- conditions/visibilité;
- prix et helpers partagés.
Command wrappers
Les dossiers acc_base/command, acc1/command et acc2/command contiennent des scripts de mêmes familles : answer key, generate results, custom command, reopen module, correction mode, etc. Les wrappers Acc1/Acc2 assurent surtout le namespace et la sélection des static data game-specific.
Unity
La même règle s'applique : le framework se trouve dans Assets/Src/Game/Data/ByModule et Assets/Src/UI/SimByModule; seules les vraies différences de contenu/assets vont sous Assets/GameSpecific/acc1 ou acc2.
Règle de maintenance
Si une modification doit fonctionner de la même façon dans Acc1 et Acc2, elle ne doit pas être dupliquée dans deux fichiers game-specific. La placer dans le framework By Module ou acc_base selon qu'elle est générique au framework ou spécifique au domaine Accounting.
Où placer une nouvelle règle Accounting
| Règle | Emplacement |
|---|---|
| Valable pour tout By Module, même un futur jeu non Accounting | _utils/by_module. |
| Spécifique à la comptabilité mais commune Acc1/Acc2 | acc_base. |
| Différence réelle Acc1 seulement | acc1. |
| Différence réelle Acc2 seulement | acc2. |
Ajouter un helper Accounting partagé
- Ajouter la fonction à
GameSpecificUtilsAccBase. - Utiliser
this.*lorsque la fonction fait partie de l'API de l'instance. - Réutiliser les constantes/preferences déjà exposées par la base.
- Vérifier que
GameSpecificUtilsAcc1et Acc2 héritent automatiquement du comportement. - Tester les deux jeux, car leurs static data peuvent différer même si le code est commun.
Éviter la duplication acc1/acc2
Si deux fichiers game-specific contiennent la même fonction à quelques valeurs près, déplacer la logique dans acc_base et passer la différence par static data/settings. Les wrappers game-specific doivent rester minces.
Debug d'un “utilsAccGameSpecific is not defined”
- Vérifier que le wrapper game-specific inclut son fichier utils.
- Vérifier que l'instance
GameSpecificUtilsAcc1/Acc2a été construite. - Vérifier l'ordre des
bridge.include(). - Dans le debugger local, inclure le même game-specific que le script réel.
- Ne pas supposer que le test runner est neutre : il inclut actuellement Acc2 dans son bootstrap.
Modèle de données et cycle de vie d’un Team Module
Document central
Le document persistant central est teamsModules. Côté Unity, BackEndBrainCloud.GetTeamModule(teamUid, actModuleUid, ...) appelle get_document avec l'UID généré par UtilsBase.GetTeamModuleUid(teamUid, actModuleUid).
Objets principaux
TeamModuleBase: module d'une équipe;TeamModuleTypeData: données métier d'un type de module;TeamModuleDecisionsData: pages et dropdowns d'un jeu de décisions;TeamModulePageData: données persistées d'une page;DataDrivenTableData,DataDrivenSectionData,DataDrivenInputData: structure éditable;ResultsByModule: résultat/correction récupéré par le client.
classDiagram
class TeamModuleBase
class TeamModuleTypeData
class TeamModuleDecisionsData
class TeamModulePageData
class DataDrivenTableData
class DataDrivenSectionData
class DataDrivenInputData
TeamModuleBase --> TeamModuleTypeData
TeamModuleTypeData --> TeamModuleDecisionsData
TeamModuleDecisionsData --> TeamModulePageData
TeamModulePageData --> DataDrivenTableData
DataDrivenTableData --> DataDrivenSectionData
DataDrivenSectionData --> DataDrivenInputData
Ce que TeamModuleTypeData peut porter
Selon le module, la structure contient notamment seed, bills, finished bills, pagesData, dynamicDropdownsData, comptes initiaux, inventory, immobilizations, contracts, seeded products/services, taxes, ownership et snapshots de contextes.
Practice / Summative
Un même Team Module peut avoir plusieurs moduleTypesData, notamment practice et summative. La plupart des custom commands reçoivent donc systématiquement moduleType en plus de teamUid et actModuleUid.
Contexts
Le framework supporte des données héritées entre contextes. Les champs savedPagesDataByContextUids, initialPagesData et les équivalents de dropdowns permettent de transporter ou préremplir l'état d'un contexte antérieur.
Versioning
Tout changement structurel du document doit être accompagné d'une migration dans _utils/version/version_teams_modules.js. Ce fichier contient déjà une chaîne de migrations successives.
Ajouter un nouveau champ TeamModuleTypeData
- Définir si le champ est généré, édité par l'étudiant, dérivé ou hérité d'un contexte précédent.
- Ajouter le champ côté serveur dans l'objet produit par Create Module Type Data.
- Ajouter le champ dans le modèle Unity
TeamModuleTypeData.cssi le client doit le lire. - Ajouter la désérialisation avec une valeur par défaut pour les vieux documents.
- Si l'étudiant peut modifier le champ, ajouter une custom command dédiée ou réutiliser le système data-driven approprié.
- Ajouter RTT si les coéquipiers doivent voir le changement immédiatement.
- Ajouter une migration dans
version_teams_modules.jssi le champ doit exister dans les documents persistés historiques. - Vérifier practice et summative séparément.
- Vérifier les backups/reopen/restart module si ce champ doit être restauré.
Choisir où mettre une donnée
| Donnée | Emplacement recommandé |
|---|---|
| Structure immuable du module | Static data / settings. |
| Donnée générée par seed pour une équipe | TeamModuleTypeData. |
| Valeurs saisies dans les pages | TeamModuleDecisionsData.pagesData. |
| Options dynamiques de dropdown | dynamicDropdownsData. |
| Résultat/correction | document teams / results structures, pas dans les décisions. |
| Donnée à partager entre contextes | saved/initial context data ou champ cumulatif explicitement géré. |
Reopen / restart / backup
Une nouvelle donnée persistante doit être testée avec restartStudentModule, restore_module_backup et reopen_module si ces flows doivent la restaurer ou la réinitialiser. C'est un trou classique lorsqu'on ajoute seulement la génération initiale.
Configuration Accounting — guide des JSON
Objectif
Cette page sert de carte des fichiers de configuration du système Accounting / ByModule. Le point important est de distinguer trois familles : les données statiques utilisées par la simulation, les settings qui activent/configurent les modules, et les definitions qui décrivent surtout les champs disponibles dans les écrans d'édition/wizards.
1. Vue générale du chargement
flowchart LR
JSON[Resources/data_byModule/*.json] --> STATIC[StaticDataByModule.Initialize]
STATIC --> CATALOG[Catalogues statiques]
STATIC --> SETTINGS[ModulesSettings / PagesSettings]
STATIC --> DEFINITIONS[Definitions pour éditeur / wizard]
CATALOG --> GAME[Runtime Accounting]
SETTINGS --> GAME
DEFINITIONS --> EDITOR[Configuration des activités/modules]
EDITOR --> MODULECFG[Configuration sauvegardée du module]
MODULECFG --> GAME
Le point d'entrée Unity est Assets/Src/Game/Data/ByModule/StaticDataByModule.cs. Il charge les fichiers du dossier Assets/GameSpecific/byModule/Resources/data_byModule via Resources.Load.
2. Les JSON principaux
| Fichier | Rôle | Quand le modifier |
|---|---|---|
modules.json |
Catalogue des modules disponibles : uid, nom, description, type de module, editor, icône/image, limites et métadonnées d'affichage. | Quand on ajoute un nouveau module ou qu'on change son identité dans le catalogue. |
modules_settings.json |
Valeurs/settings par défaut des modules : pages, préférences, bills, outils accounting, durée, options de validation, etc. | Quand on change le comportement par défaut d'un module existant ou sa configuration Accounting. |
modules_settings_definitions.json |
Décrit quels champs/préférences/bill definitions sont exposés pour chaque type de module dans les écrans de configuration. | Quand un nouveau paramètre doit devenir configurable dans le wizard/editor. |
module_pages.json |
Structure statique des pages et tableaux : pages, tables, rows, inputs, types d'inputs et outils comme journal général, grand livre, balance, états financiers, cash flow, etc. | Quand la structure visuelle ou fonctionnelle d'une page Accounting change. |
pages_settings.json |
Comportement d'une page : rangées/sections dynamiques, row editor, formules, navigation et autres options runtime. | Quand une page existante doit accepter un comportement dynamique différent. |
pages_settings_definitions.json |
Définitions des champs éditables de pages_settings. |
Quand on ajoute un nouveau setting de page configurable. |
preferences_settings_definitions.json |
Définitions des préférences de module utilisables dans l'éditeur. | Quand une nouvelle préférence doit être sélectionnable/configurable. |
bills_settings_definitions.json |
Schéma des données configurables par type de bill : type, nombre de bills, dates, comptes, taxes, partner subtype, produit/service, règles spécifiques, etc. | Quand un nouveau type de bill ou un nouveau paramètre de génération doit être exposé à la configuration. |
bills_category_settings_definitions.json |
Schéma des options par catégorie de bills. Dans la version actuelle, ces definitions décrivent les options que l'éditeur peut présenter. | Quand une catégorie de bills reçoit une nouvelle option commune. |
accounts.json |
Plan comptable statique : comptes par défaut/by-sector/custom, numéro, nom, type, unique_identifier, ordre, balance par défaut et metadata cash-flow. | Quand on ajoute/modifie un compte ou son comportement comptable. |
account_types.json |
Définitions des types/catégories de comptes. | Quand la taxonomie des comptes change. |
taxes.json |
Taxes de vente et données associées chargées dans SalesTaxes. |
Quand une taxe de vente ou sa configuration statique change. |
income_taxes.json |
Définitions générales des impôts sur le revenu. | Quand les types/règles générales d'impôt changent. |
employer_income_taxes.json |
Données de charges/taxes employeur. | Quand les règles employeur changent. |
income_tax_*_YEAR.json |
Tables fiscales annuelles fédérales/provinciales, chargées à la demande selon région et année. | Lors d'une mise à jour fiscale annuelle. |
income_taxes_codes.json |
Codes liés aux calculs/configurations d'impôt. | Quand la correspondance des codes fiscaux change. |
interests.json |
Définitions de taux/catégories d'intérêts. | Quand le calcul d'intérêts doit utiliser de nouvelles données statiques. |
immobilizations.json |
Catalogue/configuration des immobilisations. | Quand un type d'immobilisation ou ses données changent. |
contract_regularizations.json |
Données de régularisation des contrats. | Quand les règles de régularisation de contrats changent. |
regions.json |
Catalogue des régions supportées. | Quand une région est ajoutée/modifiée. |
regions_settings.json |
Comportements propres à une région : options fiscales/employeur/employé, congés et autres paramètres régionaux. | Quand une région doit avoir un comportement Accounting différent. |
act_settings.json |
Settings globaux d'activité utilisés par les modules. | Pour une configuration transversale à l'activité plutôt qu'à une page/module précis. |
constants.json |
Constantes globales ByModule utilisées par les systèmes runtime. | Quand un comportement dépend d'une constante partagée plutôt que d'un module particulier. |
dropdown_data.json |
Données de dropdown statiques utilisées dans les configurateurs. | Quand une liste de choix statique change. |
dynamic_dropdown_data_structure.json |
Structure des dropdowns dynamiques. | Quand une donnée sélectionnable dépend d'autres données/configurations. |
3. Relation entre modules_settings et les definitions
flowchart TD
DEF[modules_settings_definitions.json] -->|liste des preferences| PREF[preferences_settings_definitions.json]
DEF -->|liste des bills| BILLDEF[bills_settings_definitions.json]
BILLDEF --> WIZ[BillsDataWizardPageControl]
PREF --> WIZARD[Module settings wizard]
DEFAULT[modules_settings.json] --> MS[ModuleSettingsBase]
WIZ --> SAVED[Configuration du module/activité]
WIZARD --> SAVED
SAVED --> MS
MS --> PREFS[preferences]
MS --> BTD[bills_type_data]
MS --> BCS[bills_category_settings]
MS --> HIDDEN[bills_types_hidden_total]
À retenir : un fichier *_definitions.json ne représente pas forcément la valeur runtime. Il définit souvent comment configurer cette valeur. Les valeurs effectives d'un module finissent dans ModuleSettingsBase.
4. Configuration des bills dans un module
ModuleSettingsBase expose deux structures principales :
bills_type_data→ dictionnaire deBillTypeData/ données détaillées de génération par bill;bills_category_settings→ options communes par catégorie, par exemple le nombre de bills aléatoires à sélectionner dans une catégorie.
Le backend lit notamment _moduleSettings.bills_category_settings lors de la génération et utilise bills_type_data comme source réelle pour BillFactory.
bills_settings_definitions.json
Ce fichier est la grosse table de définition des champs disponibles pour les bills. On y retrouve notamment :
type,num_bills,min_num_entries,max_num_entries;- dates, fréquences, occurrences, délais et termes;
- comptes impactés : sales, expenses, payable, receivable, cash, loan, amortization, etc.;
- taxes : taxes de vente, déductions, taxes récupérables;
- relations : product/service, contract, immobilization, employee, supplier, investment;
- règles de création/édition et données spécifiques au type de bill.
5. Configuration des pages Accounting
flowchart LR
M[modules.json] --> UID[module uid]
UID --> MS[modules_settings.json]
MS --> ORDER[pages_order]
ORDER --> MP[module_pages.json]
MP --> STRUCT[Structure UI: page/table/input]
STRUCT --> PS[pages_settings.json]
PS --> BEHAVIOR[Rows/sections dynamiques, formules, navigation]
module_pages.json répond principalement à la question « qu'est-ce qui existe dans la page ? », tandis que pages_settings.json répond à « comment cette page se comporte ? ».
6. Plan comptable et calculs
flowchart LR
ACC[accounts.json] --> STATIC[StaticDataByModule.Accounts]
TYPES[account_types.json] --> STATIC
TAX[taxes / income_taxes / employer_income_taxes] --> STATIC
REG[regions + regions_settings] --> STATIC
STATIC --> BILL[Bills]
STATIC --> JOURNAL[General Journal]
STATIC --> LEDGER[General Ledger]
STATIC --> TB[Trial Balance]
STATIC --> FS[Financial Statements]
7. regions_settings.json : filtre régional critique
À ne pas oublier : ajouter une donnée dans son JSON principal ne suffit pas toujours à la rendre disponible dans le jeu. Pour plusieurs données Accounting, regions_settings.json agit comme un filtre/routeur par région.
Comptes
Pour les comptes, c'est particulièrement important. UtilsByModule.GetRegionAccounts() lit la région de l'activité, choisit un type via global.accounts.type_by_regions, puis prend uniquement les UIDs présents dans global.accounts.data_by_type[type]. Ensuite seulement, ces UIDs sont résolus dans accounts.json.
Donc un compte peut exister parfaitement dans accounts.json et ne jamais apparaître dans le jeu s'il n'est pas ajouté dans la liste appropriée de regions_settings.json. Après ce filtre régional, GetUsedAccounts() applique encore les visibilityConditions du compte et preferences.hiddenAccounts.
flowchart LR
A[accounts.json] --> ALL[Catalogue complet des comptes]
R[act_settings.region] --> RS[regions_settings.json]
RS --> LIST[Liste de comptes autorisés pour la région]
ALL --> FILTER[GetRegionAccounts]
LIST --> FILTER
FILTER --> VIS[visibilityConditions + hiddenAccounts]
VIS --> USED[Comptes réellement disponibles]
Ce que regions_settings.json pilote aussi
- Taxes de vente via
global.taxes: par exemple GST/QST au Québec versus HST selon la province. - Retenues / impôts employés via
global.income_taxes. - Charges employeur via
global.employer_income_taxes. - Intérêts via
global.interests. - Colonnes de certaines pages/journaux via
pages.<page_uid>.column_inputs_order. L'ordre et même les colonnes affichées peuvent changer selon la région. - Prefab de certaines pages/formulaires via
pages.<page_uid>.prefab_reference. Exemple : le formulaire de paiement des taxes utilise un contrôle différent selon la province.
Pattern utilisé
La plupart de ces sections utilisent le même pattern :
{
"type_by_regions": {
"ca_on": "ca_on"
},
"data_by_type": {
"default": [...],
"ca_on": [...]
}
}
Si la région courante n'est pas présente dans type_by_regions, le code retombe sur default.
Checklist quand on ajoute une donnée régionale
- Créer/modifier la donnée source :
accounts.json,taxes.json, tables fiscales, etc. - Vérifier
regions_settings.jsonet ajouter l'UID dans chaquedata_by_typeoù il doit être disponible. - Si une nouvelle région/variante est nécessaire, ajouter le mapping dans
type_by_regions. - Pour une page régionale, vérifier aussi
column_inputs_orderouprefab_reference. - Tester au minimum la région
defaultet une région explicitement mappée.
Exemple classique de bug : « j'ai ajouté le compte dans accounts.json, il charge sans erreur, mais il n'apparaît nulle part ». Le premier endroit à vérifier est regions_settings.json → global.accounts.
8. Règle pratique : quel fichier modifier ?
| Je veux... | Commencer par... |
|---|---|
| Ajouter un nouveau module | modules.json + modules_settings.json + sa définition/settings/pages selon le besoin. |
| Changer la structure d'une page | module_pages.json. |
| Changer le comportement d'une page | pages_settings.json. |
| Ajouter une préférence configurable | preferences_settings_definitions.json puis consommation dans le code/settings. |
| Ajouter un paramètre de bill configurable | bills_settings_definitions.json puis le code serveur qui consomme le champ. |
| Changer combien/quels bills un module génère | Configuration effective bills_type_data/bills_category_settings du module. |
| Ajouter/modifier un compte | accounts.json. |
| Mettre à jour une table fiscale annuelle | income_tax_*_YEAR.json correspondant. |
| Changer une règle propre à une région | regions_settings.json. |
Fichiers de code qui chargent cette configuration
Assets/Src/Game/Data/ByModule/StaticDataByModule.cs— chargement central des JSON dedata_byModule.Assets/Src/Game/Data/ByModule/ModuleSettingsBase.cs— désérialisation des settings runtime, incluant bills.Assets/Src/UI/SimByModule/Data/ModuleSettingsDefinition.cs— relie une définition de module aux définitions de préférences et de bills.Assets/Src/UI/SimByModule/Data/BillsDataWizardPageControl.cs— éditeur des données de bills.Assets/Src/UI/SimByModule/Data/BillDataSettingDefinitionData.cs— représentation Unity d'une définition de bill.Assets/Src/UI/SimByModule/Data/BillCategorySettingDefinitionData.cs— représentation Unity des options de catégorie.
Point important
Il n'existe pas dans cette branche de fichier statique bills_settings.json dans Resources/data_byModule. Les fichiers statiques présents sont bills_settings_definitions.json et bills_category_settings_definitions.json; les valeurs effectives des bills sont intégrées aux settings du module/activité sous bills_type_data et bills_category_settings.
Constantes, préférences et conditions By Module
Source serveur
_utils/by_module/byModule_utils.js construit un cache de constantes utilisé par l'ensemble du moteur. Les strings définies ici doivent correspondre aux clés des JSON de configuration.
Module preferences
Les préférences actuellement reconnues incluent :
num_decimals
our_company_type
company_legal_form
stock_type
has_taxes
has_deductions
has_decimals
has_formulas
uses_auxiliary_journals
has_employees
has_credit_notes
hidden_accounts
accounts_uses_last_financial_statement
uses_accounting_closing
unique_client_preference
unique_provider_preference
last_year_partners_with_empty_balance
uses_bill_id_in_general_journal
uses_new_regularizations
has_new_immobilizations
has_new_contracts
uses_regularizations
has_amortizations
regularization_accounts_count
pages
affected_regularizations_reports
has_immobilizations
has_immobilizations_taxes_report
uses_taxes_payment_form
uses_stock_regularizations
rate_num_bills_with_deductions
has_banking_transactions
has_land_immobilizations
manual_income_taxes
has_net_profit
permanent_stock_method
receive_or_send_taxes
decimal_money_tolerance_range
uses_bank_reconciliation
bank_reconciliation_form_numbers
module_time_limit
single_owner
partnership
joint_stock_a
joint_stock_ab
retry_summative
has_debts
has_interests
module_uid
ignore_grading_underline
ignore_grading_cash
ignore_grading_indent
ignore_grading_bold
auto_report_to_ledger
Page preferences
ignored_options;ignored_grading_column_uids;ignored_grading_relation_uids;money_tolerance_range;num_columns;has_dynamic_section.
UIDs par défaut
Le framework utilise default comme UID standard pour table, section et row lorsque la structure ne nécessite pas d'identifiant spécifique.
Conditions de visibilité
Les conditions reconnues comprennent condition_equal, or et and, avec le préfixe not_ pour l'inversion. Côté Unity, les classes VisibilityCondition* représentent ces conditions.
Résolution d'une préférence
Ne pas lire un JSON arbitrairement depuis un calcul. Le moteur passe par les helpers getModulePreference(), getPagePreference() et les settings du contexte courant, ce qui permet de gérer valeurs par défaut et overrides.
Où définir la valeur
Les commentaires du code indiquent la source attendue : plusieurs defaults sont dans constants.json, les préférences de pages dans pages_settings.json et certaines valeurs globales d'activité dans act_settings.json. La page protégée Configuration Accounting — guide des JSON reste la référence pour le détail des fichiers.
Ajouter une nouvelle préférence By Module
1. Déterminer le scope
- valeur globale par défaut →
constants.json; - valeur par module →
modules_settings.json/ definitions; - valeur par page →
pages_settings.json/ definitions; - valeur activité/région →
act_settings/regions_settingsselon le besoin.
2. Ajouter la constante string côté serveur
Ajouter la clé dans la table modulesPreferences ou pagesPreferences de byModule_utils.js. La string doit correspondre exactement au JSON.
3. Définir une valeur par défaut
Une preference manquante ne doit pas produire un comportement aléatoire. Ajouter le default dans la source appropriée et vérifier getModulePreference()/getPagePreference().
4. Consommer via les helpers
Ne pas lire directement le JSON statique dans un moteur métier si l'helper de preference gère déjà overrides/context. Cela permet de conserver les régions et settings spécifiques.
5. Si Unity en a besoin
Ajouter la constante/lecture côté client seulement si le frontend doit réellement changer son comportement. Éviter de dupliquer une preference purement serveur.
Ajouter une condition de visibilité
Les conditions utilisent notamment condition_equal, and, or et le préfixe not_. Si un nouveau type de condition est nécessaire, il faut mettre à jour le parser/évaluateur côté Unity et, si utilisé côté serveur, sa logique équivalente.
Test minimal d'une nouvelle préférence
- default absent d'override;
- override module;
- override région si applicable;
- valeur false/0 explicite — ne pas la confondre avec “absente”;
- ancien module sans la nouvelle clé;
- Acc1 + Acc2 si la preference est partagée.
UI data-driven, pages, tables et prefabs By Module
Rôle
L'UI By Module est générée à partir de module_pages.json, des pages_settings et des données runtime du TeamModuleTypeData. La structure visuelle n'est donc pas entièrement codée en dur dans les prefabs.
Hiérarchie UI
flowchart TD
M[TeamModuleControl] --> PAGE[ModulePageControlBase]
PAGE --> TABLE[ModulePageTableControlBase]
TABLE --> SEC[Section / EditableSection]
SEC --> ROW[Row / EditableRow]
ROW --> CELL[CellControlBase]
CELL --> INPUT[InputControlBase]
INPUT --> TEXT[TextInput]
INPUT --> DROP[Dropdown / AutoComplete]
INPUT --> CHECK[Checkbox / Toggle]
Classes structurantes
| Responsabilité | Classes |
|---|---|
| Liste/modules | TeamModuleListControl, TeamModuleItemControl, TeamModuleControl, TeamModuleMenuControl |
| Pages | ModulePageControlBase, ModulePageInputsControl, ModulePageTableControl* |
| Tables data-driven | DataDrivenTableControl, DataDrivenSectionControl, DataDrivenRowControl, DataDrivenInputsControl |
| Rows/sections dynamiques | EditableRowControlBase, EditableSectionControlBase, CustomRowControl |
| Inputs | InputControlBase, InputCellControl, TextInputControl, DropdownInputControl, autocomplete, checkbox |
| Correction | CorrectedTeamModuleWindowControl, ModuleErrorsPopupControl, CellErrorButtonControl |
Types de pages
Le modèle Unity expose plusieurs classes de données : ModulePageDataInputs, ModulePageDataTable, ModulePageDataTableWithInputs, ModulePageDataTableWithDropdown, etc. Le type configuré dans la static data détermine quel contrôle Unity est utilisé.
Pages Accounting spécialisées
En plus du moteur générique, le dossier Assets/Src/UI/SimByModule/UI/ contient les vues spécialisées : trial balance, balance sheet, income statement, statement of equity, bank reconciliation, formulaires de taxes, reports d'employés, immobilisations, contrats, bills et autres documents.
Frontend JSON → runtime
flowchart LR
MP[module_pages.json] --> SD[StaticDataByModule]
PS[pages_settings.json] --> SD
SD --> PC[ModulePageControl]
TM[TeamModuleTypeData.pagesData] --> PC
PC --> INPUTS[Rows / Cells / Inputs]
INPUTS --> CTRL[TeamByModuleController]
CTRL --> SERVER[Custom Command]
Quand modifier quoi
- Structure d'une page :
module_pages.json. - Comportement dynamique/validation :
pages_settings.jsonet definitions associées. - Nouveau rendu visuel réutilisable : classe/prefab sous
SimByModule/UI. - Donnée persistée : modèle
DataDriven*+ custom command serveur. - Nouvelle page Accounting réellement spécialisée : ajouter le contrôle dédié et le brancher dans le système de page correspondant.
Ajouter un nouveau type de page ou de contenu
Avant de créer un nouveau type, vérifier si la page peut être exprimée avec les types existants : table, table_with_dropdown, table_with_dynamic_dropdown, table_with_inputs, inputs ou table_inputs_with_dropdown.
Si un type existant suffit
- Déclarer la page dans
module_pages.json. - Configurer structure/inputs/columns et
pages_settings. - Ajouter un prefab custom seulement si le type
inputsen a besoin via les références de prefab. - Vérifier rendu normal + rendu corrected.
Si un vrai nouveau ModulePageType est nécessaire
- Ajouter la string dans
ModulePageTypeConstants. - Ajouter la valeur dans
ModulePageBase.ModulePageType. - Mettre à jour
GetModulePageType()etGetModulePageTypeName(). - Ajouter le modèle
ModulePageData*si la structure JSON diffère. - Mettre à jour
Factory.CreateModulePageData(type). - Créer/adapter le
ModulePageControlBasecorrespondant. - Ajouter le prefab template aux listes utilisées par
ReferencedModuleWindowControletCorrectedTeamModuleWindowControl. - Mettre à jour
UtilsByModule.GetModulePageControlPoolType()si le nouveau type peut partager un pool. - Ajouter le rendu de contenu dans
AllocModulePageContentTemplate(). - Ajouter Answer Key + Grading si la page est évaluée.
Ajouter un nouvel input data-driven
Un nouvel input implique généralement quatre zones : modèle de définition static data, modèle runtime de valeur/options, contrôle Unity, et custom command de sauvegarde. Si l'input possède des options de correction, vérifier aussi le grading options.
Rendu corrected à ne pas oublier
Une page qui fonctionne pendant la prise de décision mais plante/est vide en correction est souvent un nouveau type branché seulement dans la fenêtre normale. Toujours tester CorrectedTeamModuleWindowControl et les CellErrorButtonControl/expectedValues.
Checklist UI By Module
- page normale;
- page corrected;
- practice;
- summative;
- resize/window;
- export Excel si applicable;
- dynamic rows/sections;
- formules si l'input les supporte;
- RTT avec deux clients.
Custom Commands et édition des pages By Module
Routeur serveur
La majorité des actions étudiantes passent par _utils/command/utils_custom_command_by_module.js. La fonction executeCommandByModule() route commandUid vers la fonction métier appropriée.
Commandes majeures
| Fonction | Responsabilité |
|---|---|
startStudentModule |
Initialise/démarre un module pour l'équipe. |
saveStudentModuleRowInputValue |
Sauvegarde une valeur d'input dans une row. |
saveStudentModuleRowInputOptions |
Sauvegarde les options d'un input. |
saveStudentModuleInputFormula |
Sauvegarde une formule; voir page dédiée Formules. |
add/remove/edit ... Row |
Gestion des rows dynamiques et de leur ordre. |
add/remove/edit ... Section |
Gestion des sections dynamiques. |
saveDynamicDropdownData |
Persistance des données de dropdown dynamique. |
getModuleGrade |
Calcule/récupère la correction. |
getTeamModuleTypeResults |
Retourne les résultats déjà générés. |
restartStudentModule |
Réinitialise un essai de module selon les règles. |
ignoreErrorFromTeamModuleResults |
Permet l'override/ignorance d'une erreur de correction. |
Rows et sections dynamiques
Lorsqu'une row/section temporaire doit être persistée, le serveur peut lui attribuer un nouvel ObjectId. Il renvoie alors les alias et index à l'UI afin que le client remplace l'UID local. Le serveur maintient aussi les références de formules associées.
Synchronisation du ledger
Après certaines modifications de pages, le code vérifie utilsByModule.shouldSyncLedger(moduleSettings, pageUid). Si nécessaire, il appelle rebuildLedgerFromJournals() puis envoie sendLedgerPageChangedMessage(). Une édition dans un journal peut donc modifier indirectement la page ledger.
RTT
Chaque commande émet un message précis, par exemple team_module_row_input_options_changed, team_module_row_removed, dynamic_dropdown_data_changed ou team_module_input_formula_changed. Les gros objets ne sont pas toujours envoyés directement : le message get_data peut demander au client de relire seulement un chemin d'entité.
Côté Unity
TeamByModuleController envoie ces commandes, reçoit les messages et met à jour TeamModuleTypeData. Les classes DataDrivenTableControl, DataDrivenRowControl, DataDrivenSectionControl et les contrôles d'input construisent l'interface à partir des données statiques + runtime.
Ajouter une Custom Command By Module de A à Z
1. Définir commandUid et payload
Le commandUid doit être stable et décrire l'action, pas le contrôle UI qui la déclenche. La payload contient généralement teamUid, actModuleUid, moduleType et les données minimales de l'opération.
2. Ajouter le wrapper game-specific si nécessaire
Acc1/Acc2 passent par leur <game>_custom_command, qui délègue au routeur By Module commun.
3. Brancher executeCommandByModule()
Ajouter le nouveau cas dans utils_custom_command_by_module.js. Extraire la logique complexe dans un helper séparé si elle dépasse une opération simple de page.
4. Modifier le Team Module
Lire le bon teamsModules, sélectionner moduleTypesData[moduleType], valider que la page/row/section visée existe puis appliquer la mutation.
5. Recalculer les dépendances
Si l'action touche un journal/formule/dropdown/ledger, exécuter les rebuilds nécessaires avant la sauvegarde. Ne pas compter sur l'UI pour recomposer un état dérivé.
6. Sauvegarder et envoyer RTT
Choisir un message précis dans MessageTypeByModule. Pour une grosse structure, préférer un refresh ciblé par chemin plutôt qu'une payload massive.
7. Ajouter le handler Unity
TeamByModuleController doit mettre à jour la structure locale exactement comme le serveur. Tester avec deux étudiants dans la même équipe.
Concurrence rows/sections
Les rows/sections dynamiques peuvent être créées avec un UID temporaire côté client puis recevoir un ObjectId serveur. Toute nouvelle commande structurelle doit gérer ce remplacement et les références de formules associées.
Checklist custom command
- mauvais moduleType rejeté proprement;
- UID row/section inexistant géré;
- document sauvegardé avant RTT;
- ledger/formules recalculés si dépendants;
- second client synchronisé;
- relog recharge exactement le même état;
- practice et summative vérifiés.
Système de formules
Vue d’ensemble
Le système de formules permet à un étudiant de calculer automatiquement la valeur d'une cellule à partir d'autres cellules numériques d'un tableau de module. Une formule est une liste de références signées : chaque référence est ajoutée ou soustraite du total.
Le calcul est effectué dans Unity, tandis que la définition de la formule est persistée côté backend. La valeur calculée reste stockée dans DataDrivenInputData.value; la structure de la formule est stockée séparément dans DataDrivenInputData.formula.
1. Architecture générale
flowchart LR
USER[Étudiant] --> EDITOR[DataDrivenTableEditorControl]
EDITOR --> TABLE[DataDrivenTableControl]
TABLE --> CELL[InputCellControl]
TABLE --> FORMULA[DataDrivenInputFormulaData]
FORMULA --> REF[DataDrivenInputFormulaReferenceData]
TABLE --> CALC[Calcul + graphe de dépendances]
CALC --> VALUE[DataDrivenInputData.value]
VALUE --> CELL
TABLE --> CTRL[TeamByModuleController]
CTRL --> CMD[save_student_module_input_formula]
CMD --> BACK[Backend Node.js / BrainCloud]
BACK --> STORE[(teamsModules)]
BACK --> EVENT[team_module_input_formula_changed]
EVENT --> CTRL
CTRL --> TABLE
2. Modèle de données
Une cellule peut contenir une formule optionnelle. La formule contient une liste de références; chaque référence identifie complètement une cellule et contient son signe.
classDiagram
class DataDrivenInputData {
string value
Dictionary options
DataDrivenInputFormulaData formula
}
class DataDrivenInputFormulaData {
List references
Serialize()
Deserialize()
Clone()
}
class DataDrivenInputFormulaReferenceData {
string pageUid
string tableUid
string sectionUid
string rowUid
string inputUid
int sign
}
DataDrivenInputData --> DataDrivenInputFormulaData
DataDrivenInputFormulaData --> "0..*" DataDrivenInputFormulaReferenceData
Adresse complète d’une référence
Un inputUid seul n'est pas suffisant, puisque le même input peut exister dans plusieurs sections ou rangées. Une référence conserve donc pageUid, tableUid, sectionUid, rowUid, inputUid et sign. Le signe vaut 1 pour additionner et -1 pour soustraire.
3. Création d’une formule dans l’interface
sequenceDiagram
actor U as Étudiant
participant E as DataDrivenTableEditorControl
participant T as DataDrivenTableControl
participant C as InputCellControl
U->>C: Sélectionne la cellule résultat
U->>E: Active le mode formule
E->>T: onFormulaModeValueChanged(true)
T->>T: signe initial = PLUS
T->>C: met les cellules compatibles en mode sélection
U->>E: choisit + ou -
E->>T: onFormulaReferenceSignChanged(sign)
U->>C: clique une cellule référence
C->>T: onSelected(inputUid)
T->>T: SelectFormulaReference(...)
T->>T: ajoute / change / retire la référence
T->>C: outline vert (+) ou rouge (-)
U->>E: désactive le mode formule / Enter
E->>T: onFormulaModeValueChanged(false)
T->>T: sauvegarde la formule
Comportement lors d’un clic sur une référence
- si la référence n'existe pas, elle est ajoutée;
- si elle existe avec l'autre signe, son signe est remplacé;
- si elle existe déjà avec le même signe, elle est retirée;
- la cellule résultat ne peut pas se référencer elle-même;
- seules les cellules numériques compatibles sont sélectionnables en mode formule;
- une cellule qui dépend directement de la cellule résultat est rendue non sélectionnable afin d'éviter une dépendance circulaire directe.
Les références positives sont affichées avec un contour vert et les références négatives avec un contour rouge via InputCellControl.SetFormulaOutline().
4. Calcul d’une formule
flowchart TD
F[DataDrivenInputFormulaData] --> LOOP[Pour chaque référence]
LOOP --> GET[TryGetInputData]
GET --> PARSE[Parse inputData.value en double]
PARSE -->|échec| ZERO[Valeur = 0]
PARSE --> MOD{value_modifier présent ?}
ZERO --> MOD
MOD -->|Oui| APPLY[referenceValue *= valueModifier]
MOD -->|Non| SIGN
APPLY --> SIGN[Appliquer sign +1 / -1]
SIGN --> SUM[Ajouter au total]
SUM --> LOOP
LOOP -->|fin| VALUE[Écrire le résultat dans inputData.value]
VALUE --> UI[Mettre à jour InputCellControl]
VALUE --> TOTAL[Mettre à jour total row / journal si applicable]
Les valeurs impossibles à parser sont traitées comme 0. Si l'option serveur value_modifier est présente sur une cellule référencée, elle est appliquée avant le signe de la référence.
5. Propagation des dépendances
Le système construit un index inverse des dépendances dans _formulaListenersByReference. La clé d'une cellule est construite sous la forme sectionUid|rowUid|inputUid.
flowchart LR
A[Cellule A change] --> LOOKUP[_formulaListenersByReference A]
LOOKUP --> B[Formule B dépend de A]
LOOKUP --> C[Formule C dépend de A]
B --> RB[RecalculateFormula B]
C --> RC[RecalculateFormula C]
RB -->|valeur changée| NEXTB[Notifier les formules dépendantes de B]
RC -->|valeur changée| NEXTC[Notifier les formules dépendantes de C]
NEXTB --> D[Formule D]
NEXTC --> E[Formule E]
La propagation est récursive : lorsqu'une formule change de valeur, les formules qui dépendent d'elle sont recalculées à leur tour. Un HashSet de clés déjà visitées empêche la propagation de boucler indéfiniment pendant un même cycle de notification.
Initialisation
Lors du chargement ou d'un refresh important, InitializeFormulasByReference() reconstruit entièrement l'index inverse, puis RefreshFormulasByReferenceAndRecalculateAll() recalcule toutes les cellules possédant une formule.
6. Sauvegarde et synchronisation backend
sequenceDiagram
participant T as DataDrivenTableControl
participant C as TeamByModuleController
participant B as Backend Node.js
participant DB as teamsModules
participant M as Messages équipe/prof
T->>C: SaveTeamModuleInputFormula(..., formula)
C->>B: custom command save_student_module_input_formula
B->>B: localise page/table/section/row/input
alt formule avec références
B->>DB: inputData.formula = formula
else formule vide
B->>DB: delete inputData.formula
end
B->>B: updateFormulaReferences(...)
B->>DB: saveDocumentData
B->>M: team_module_input_formula_changed
M-->>C: message temps réel
C-->>T: OnTeamModuleInputFormulaChanged(...)
T->>T: reconstruit listeners + recalcule
Le backend persiste uniquement la définition de la formule. Si la liste de références est vide, la propriété formula est supprimée de l'input.
7. Sections et rangées dynamiques
Une section ou une rangée temporaire peut recevoir un nouvel UID lors de sa création côté serveur. Dans ce cas, les références de formules existantes doivent suivre le nouvel identifiant.
utilsByModule.updateFormulaReferences() parcourt les inputs de la table, des sections et des rangées et remplace les anciens sectionUid/rowUid par les UID persistés.
Lorsqu'une rangée ou une section est supprimée, les références qui pointent vers celle-ci sont retirées. Si une formule ne possède ensuite plus aucune référence, la formule est supprimée.
8. Activation et tutoriel
Le système est activé par la préférence has_formulas. Lorsque cette option est désactivée, les contrôles de formule ne sont pas affichés.
Deux séquences de tutoriel existent : ModuleFormulaTutorial introduit l'activation du mode formule, et ModuleFormulaReferencesTutorial explique les boutons + et - et la sélection des références.
Fichiers principaux
Assets/Src/UI/SimByModule/DataDrivenTableControl.cs— logique principale, calcul et dépendances.Assets/Src/UI/SimByModule/DataDrivenTableEditorControl.cs— toggle formule et boutons + / -.Assets/Src/UI/SimByModule/UI/InputCellControl.cs— interaction cellule et contours visuels.Assets/Src/Game/Data/ByModule/DataDrivenInputData.cs— valeur calculée et formule optionnelle.Assets/Src/Game/Data/ByModule/DataDrivenInputFormulaData.cs— liste des références.Assets/Src/Game/Data/ByModule/DataDrivenInputFormulaReferenceData.cs— adresse complète et signe.Assets/Src/UI/SimByModule/Controllers/TeamByModuleController.cs— sauvegarde et réception de l'événement.Assets/Src/UI/SimByModule/Tutorial/ModuleFormulaTutorial.cs— tutoriels.master/node.js/.../utils_custom_command_by_module.js— persistance backend.master/node.js/.../byModule_utils.js— mise à jour/suppression des références lors des changements d'UID.
Points d’attention
- Le résultat numérique est dans
value; la formule ne remplace pas ce champ. - Une formule doit utiliser l'adresse complète de la cellule, pas seulement
inputUid. - Toute modification d'UID dynamique doit mettre à jour les références persistées.
- Lorsqu'une cellule référencée change, il faut notifier les dépendances pour maintenir les formules en cascade.
- Lorsqu'une formule est modifiée depuis un autre client, l'événement temps réel doit reconstruire ses listeners avant le recalcul.
Création du Module Type Data
Point d'entrée
Le moteur est _utils/by_module/byModule_utils_create_module_type_data.js. Il fabrique les données déterministes d'un module à partir du seed, des act settings, module settings et page settings.
flowchart TD
IN[seed + settings] --> INIT[initialisation RNG et contexte]
INIT --> GEN[génération données métier]
GEN --> BILL[bills / partenaires / produits]
GEN --> INV[inventory]
GEN --> IMM[immobilizations]
GEN --> CON[contracts]
INIT --> PREV[contextes précédents]
PREV --> FIX[fixed pages]
PREV --> INITIAL[initial pages]
GEN --> PRE[precomputed pages]
FIX --> OUT[TeamModuleTypeData]
INITIAL --> OUT
PRE --> OUT
Initial pages
getInitialPagesData() lit current_module.initial_pages_data. Une référence préfixée par previous_ permet de réutiliser une page d'un contexte antérieur sous l'UID du contexte courant.
Fixed et precomputed data
utilsByModule.getFixedData() extrait des données fixes depuis l'answer key précédente. getPrecomputedData() peut calculer des données attendues à l'avance à partir de l'answer key courante.
Données cumulatives
Le moteur peut transporter entre contextes l'inventaire, les immobilisations/dépréciations, les contrats et des snapshots de pages/dropdowns. C'est ici qu'il faut chercher un bug où un nouveau module démarre avec de mauvaises valeurs héritées.
Fichiers liés
byModule_utils_create_module_type_data.js;functions/byModule_utils_create_module_type_data_functions.js;byModule_utils.js;classes/InventoryProduct.js;classes/bills/*etBillFactory.js;acc_base/utils/acc_base_utils.js.
Validation
Utiliser tests/unit/by_module_tests/create_module_type_data_tests et, pour une reproduction complète, testGeneralFunctionnalities(moduleUid) dans le debugger local des Server Scripts.
Carte interne du moteur de création
Le fichier principal est byModule_utils_create_module_type_data.js, avec les helpers dans functions/byModule_utils_create_module_type_data_functions.js. Pour debugger un module qui “naît mal”, il faut identifier à quelle étape la mauvaise valeur apparaît.
| Fonction | Responsabilité |
|---|---|
byModuleCreateModuleTypeData() |
Entrée publique appelée avec seed + settings. |
createModuleTypeData() |
Orchestration principale. |
initCreateModuleTypeData() |
Initialise RNG, settings, dates, static data et contexte de calcul. |
getContextData() |
Résout les données nécessaires au contexte courant. |
createInitialModuleTypeData() |
Construit les structures initiales métier. |
createInventory() |
Initialise l'inventaire lorsque le module utilise du stock. |
createBills() |
Génère les bills du contexte via settings/factories. |
postProcessContextAccounts() |
Finalise les comptes après génération du contexte. |
getInitialPagesData() |
Construit les pages initiales, y compris les références previous_*. |
initPagesData() |
Crée la structure runtime des pages/inputs. |
getCurrentDepreciations() |
Récupère les amortissements à propager. |
getCumulativeDepreciationFromYear() |
Calcule la dépréciation cumulée à transporter. |
Ajouter une nouvelle donnée générée de A à Z
- Définir sa source : static data, preference, previous context ou RNG.
- Définir où elle vit dans
TeamModuleTypeData. - Initialiser la valeur dans le moteur de création au bon moment.
- Si elle dépend d'un contexte précédent, ajouter la logique de copie/translation dans
getContextData()/getInitialPagesData()ou l'helper spécialisé. - Si elle dépend du seed, utiliser le RNG existant; ne pas appeler
Math.random(). - Si elle alimente les bills, la créer avant
createBills(). - Si elle alimente l'Answer Key, vérifier que le moteur de correction retrouve exactement la même structure.
- Ajouter la désérialisation Unity si le client doit la lire.
- Ajouter une migration teamsModules si des documents existants doivent recevoir le nouveau champ.
- Ajouter un test dans
create_module_type_data_tests.
Déterminisme : règle absolue
Le seed sert à pouvoir recréer les mêmes données, Answer Key et résultats. Une nouvelle branche de génération ne doit pas introduire un appel aléatoire hors du RNG contrôlé ni changer silencieusement l'ordre des tirages d'un module existant sans mesurer l'impact.
Contextes précédents
Les champs previous_*, savedPagesDataByContextUids, savedDynamicDropdownsDataByContextUids, inventory, immobilizations et contracts peuvent traverser les contextes. Avant de créer une nouvelle donnée “cumulative”, définir explicitement :
- est-elle copiée telle quelle ?
- recalculée ?
- transformée vers l'UID du nouveau contexte ?
- ignorée en practice/summative ?
Debug pas à pas
- Fixer un seed reproductible.
- Logger/inspecter les settings résolus.
- Comparer les données seedées avant bills.
- Comparer après
createBills(). - Inspecter inventory/immobilizations/contracts.
- Inspecter
initialPagesDataet dynamic dropdowns. - Seulement ensuite lancer Answer Key.
Le helper testGeneralFunctionnalities(moduleUid) du debugger local est idéal pour rejouer cette chaîne sans passer par Unity.
Pipeline des Bills — architecture et ajout d’un nouveau type
Comment ça fonctionne
Le système de bills traverse la configuration, le backend Node.js/BrainCloud, la sérialisation, les données Unity et l'UI. Ajouter un nouveau bill seulement dans un de ces endroits produit typiquement un bill qui n'est jamais généré, qui ne peut pas être désérialisé, ou qui s'affiche avec le mauvais template.
Cette page décrit le pipeline complet et sert surtout de checklist lorsqu'un nouveau type de bill est ajouté.
1. Architecture complète
flowchart LR
DEF[bills_settings_definitions.json] --> WIZ[Configuration module / wizard]
MOD[modules_settings_definitions.json] --> WIZ
WIZ --> CFG[bills_type_data + bills_category_settings]
CFG --> CREATE["createBills()"]
CREATE --> CTX[BillCreationContext]
CTX --> FACTORY[BillFactory.BILL_MAPPING]
FACTORY --> CLASS[Classe Bill JS concrète]
CLASS --> BCTX[BillContext]
BCTX --> SERIAL[Serialized billsData + billsOrder + partners]
SERIAL --> TMTD[Unity TeamModuleTypeData]
TMTD --> BILL[Bill.cs]
BILL --> DATA[BillDataBase.CreateBillData]
DATA --> WINDOW[BillWindowControl]
WINDOW --> TEMPLATE[BillTemplateType / BillControlBase]
TEMPLATE --> USER[Bill affiché à l'utilisateur]
2. Phase A — configuration
La configuration d'un bill commence dans bills_settings_definitions.json. Chaque clé représente un bill configurable et décrit notamment son category, son num_bills, ses conditions de visibilité et ses champs supplémentaires.
modules_settings_definitions.json choisit ensuite quelles definitions de bills sont disponibles pour chaque type de module. Les valeurs réellement choisies finissent dans les settings du module sous bills_type_data. Les options communes à une famille sont dans bills_category_settings.
flowchart TD
BSD[bills_settings_definitions.json] --> BILLDEF[Definition du type]
MSD[modules_settings_definitions.json] --> EXPOSE[Expose le type dans le module]
BILLDEF --> WIZ[BillsDataWizardPageControl]
EXPOSE --> WIZ
WIZ --> BTD[bills_type_data]
WIZ --> BCS[bills_category_settings]
BTD --> SERVER[Backend de génération]
BCS --> SERVER
3. Phase B — génération backend
Le point de départ serveur est createBills() dans byModule_utils_create_module_type_data_functions.js. Il initialise BillCreationContext, regroupe la configuration par type, applique les catégories aléatoires, puis génère les types dans un ordre déterministe.
Ordre de calcul
BillCreationContext._billTypeComputingOrder n'est pas décoratif. Plusieurs bills dépendent de bills créés avant eux : checks, credit notes, reports, notes receivable, régularisations, etc. Un nouveau type qui dépend d'un autre doit donc être placé après sa dépendance.
sequenceDiagram
participant C as createBills()
participant BC as BillCreationContext
participant F as BillFactory
participant B as Classe Bill
participant X as BillContext
C->>BC: init(random, settings, context, dates)
C->>C: groupe bills_type_data par type
loop _billTypeComputingOrder
C->>F: createBills(billTypeData)
F->>F: BILL_MAPPING[type]
F->>B: new ConcreteBill(...)
B-->>BC: addBill(uid, bill)
end
C->>X: init(... bills générés ...)
X->>X: post-processing / tri / sérialisation
X-->>C: billsData + billsOrder + partners + taxes
Factory backend
BillFactory.BILL_MAPPING est le dispatch principal string type → fonction de création. Une entrée peut pointer directement vers une fonction ou passer par conditionallyCreateBills(...) pour respecter une feature flag/préférence comme employés, taxes, banking, immobilisations ou forme juridique.
Les fonctions de création choisissent les partenaires, dates, produits/services, montants et objets dépendants, construisent la classe de bill puis appellent BillCreationContext.addBill().
4. Phase C — classes et sérialisation serveur
Les classes serveur se trouvent sous :
master/node.js/backend/braincloud/server_scripts/_utils/by_module/classes/bills/
Il existe des classes de base et des familles spécialisées : checks, reports, contracts, immobilizations, etc. Une classe concrète est nécessaire lorsqu'un type possède une structure, un calcul ou des références spécifiques qui ne peuvent pas être représentés par une classe existante.
flowchart TD
BASE[BillBase JS] --> EXIST{Structure existante suffisante ?}
EXIST -->|Oui| REUSE[Réutiliser classe existante]
EXIST -->|Non| NEW[Créer ConcreteBill.js]
NEW --> SER["serialize()"]
NEW --> DES["deserialize()"]
NEW --> CALC[calculs / références spécifiques]
REUSE --> CTX[BillContext]
SER --> CTX
CTX --> JSON[bills.billsData]
BillContext._deserializeBills() utilise utilsByModule.getBillClassFromType() pour reconstruire les bonnes classes à partir de JSON déjà sérialisé. C'est donc un deuxième mapping backend à ne pas oublier lorsqu'un nouveau type nécessite une classe.
5. Phase D — chargement dans Unity
TeamModuleTypeData.Deserialize() charge d'abord les partenaires, services, dépenses, contrats, immobilisations, taxes et autres dépendances. Ensuite il parcourt bills.billsData et construit chaque Bill.
Bill.Deserialize() lit le billType, puis appelle BillDataBase.CreateBillData(billType) afin de créer la bonne structure de données Unity avant de désérialiser billData.
sequenceDiagram
participant JSON as Team module JSON
participant T as TeamModuleTypeData
participant B as Bill
participant F as BillDataBase.CreateBillData
participant D as Concrete BillData
JSON->>T: billsData + partners + dépendances
T->>B: new Bill + Deserialize(...)
B->>B: BillBase.Deserialize billType
B->>F: CreateBillData(billType)
F-->>B: instance Concrete BillData
B->>D: Deserialize(billData, dependencies...)
B-->>T: Bill complet
T->>T: bills[uid] + billsOrder
6. Phase E — affichage Unity
BillWindowControl.GetBillTemplateTypeFromBillType() choisit le template UI à utiliser. Plusieurs types partagent le même template : Sales, Check, OperatingCost, Memo, Letter, Payslip, CreditNote, etc.
Le template est ensuite alloué depuis un pool et reçoit le bill via BillControlBase.SetData(). Les contrôles spécialisés castent le billData lorsque des champs spécifiques doivent être affichés.
flowchart LR
B[Bill + BillData] --> MAP[BillWindowControl.GetBillTemplateTypeFromBillType]
MAP --> SALES[Sales]
MAP --> CHECK[Check]
MAP --> OP[OperatingCost]
MAP --> LETTER[Letter]
MAP --> MEMO[Memo]
MAP --> REPORT[Report spécialisé]
SALES --> SET[BillControlBase.SetData]
CHECK --> SET
OP --> SET
LETTER --> SET
MEMO --> SET
REPORT --> SET
SET --> UI[UI finale]
7. État « finished »
Le fait qu'un étudiant ait terminé de traiter un bill est séparé du bill lui-même. Unity conserve finishedBills dans TeamModuleTypeData. BillWindowControl permet de modifier l'état et le backend persiste la liste via la custom command gérée par saveTeamModuleFinishedBills().
8. Ajouter un nouveau type de bill — checklist obligatoire
Exemple : ajout d'un type serveur new_bill.
| # | Fichier / zone | Modification | Obligatoire ? |
|---|---|---|---|
| 1 | Assets/.../data_byModule/bills_settings_definitions.json |
Créer la definition new_bill avec category, num_bills, visibilité et champs nécessaires. |
Oui si le bill est configurable/généré par settings. |
| 2 | modules_settings_definitions.json |
Ajouter new_bill dans la liste bills des types de modules qui doivent pouvoir le configurer. |
Oui pour l'exposer dans le wizard concerné. |
| 3 | BillCreationContext.js |
Ajouter new_bill dans _billTypeComputingOrder, à l'endroit correct selon ses dépendances. |
Oui pour un bill généré par le pipeline standard. |
| 4 | BillFactory.js |
Ajouter BILL_MAPPING["new_bill"] et implémenter/réutiliser la fonction de création. |
Oui. |
| 5 | classes/bills/.../*.js |
Créer une classe JS si le bill ne peut pas réutiliser une classe existante; implémenter sérialisation/désérialisation et logique spécifique. | Conditionnel. |
| 6 | BillFactory.js includes |
Inclure la nouvelle classe serveur si une nouvelle classe est créée. | Conditionnel. |
| 7 | byModule_utils.js → getBillClassFromType() |
Mapper new_bill vers la classe JS utilisée pour désérialiser les bills existants/contextes. |
Oui si nouvelle classe/type sérialisé. |
| 8 | Assets/Src/Game/Data/ByModule/BillBase.cs |
Ajouter le nom string, l'enum BillType, GetBillNameFromType(), GetBillTypeFromName() et vérifier les switches de taxes/partenaires/type simplifié. |
Oui. |
| 9 | Assets/Src/UI/SimByModule/Data/Runtime/BillDataBase.cs |
Ajouter le mapping dans CreateBillData() vers une classe existante ou une nouvelle classe BillData.... |
Oui. |
| 10 | Assets/Src/UI/SimByModule/Data/Runtime/BillData*.cs |
Créer la classe data Unity si le JSON billData a une forme ou des calculs propres. |
Conditionnel. |
| 11 | Assets/Src/UI/SimByModule/UI/BillWindowControl.cs |
Mapper le nouveau BillType vers le bon BillTemplateType. |
Oui. |
| 12 | BillControlBase.cs / contrôle spécialisé |
Vérifier titre, sender/receiver, taxes, total et raw data. Créer/modifier un contrôle si le template existant ne suffit pas. | Conditionnel mais à vérifier systématiquement. |
| 13 | Prefab/template Unity du bill | Créer ou brancher un prefab si un nouveau BillTemplateType ou une mise en page spécifique est nécessaire. |
Conditionnel. |
| 14 | Textes/localisation | Ajouter les clés utilisées par GetSimplifiedBillTypeName(), titre, memo, descriptions ou labels spécialisés. |
Selon UI. |
| 15 | Accounting switches / Utils | Rechercher le nouveau type dans les règles de taxes, partenaires, journaux, inventory, regularizations, reports et autres traitements. | À auditer systématiquement. |
9. Fichiers critiques — ne pas oublier
Backend Node.js / BrainCloud
backend/braincloud/server_scripts/_utils/by_module/functions/byModule_utils_create_module_type_data_functions.js— orchestrationcreateBills().backend/braincloud/server_scripts/_utils/by_module/contexts/BillCreationContext.js— contexte global, ordre de calcul, partenaires/dates/dépendances.backend/braincloud/server_scripts/_utils/by_module/factories/BillFactory.js— mapping type → création et includes des classes.backend/braincloud/server_scripts/_utils/by_module/contexts/BillContext.js— post-processing, désérialisation, ordre et sérialisation finale.backend/braincloud/server_scripts/_utils/by_module/byModule_utils.js—getBillClassFromType()et plusieurs règles Accounting transversales.backend/braincloud/server_scripts/_utils/by_module/classes/bills/— implémentations concrètes.backend/braincloud/server_scripts/_utils/command/utils_custom_command_by_module.js— actions runtime persistées, notammentfinishedBillset autres mutations de module.
Unity / Game
Assets/Src/Game/Data/ByModule/BillBase.cs— enum et conversion string/type, taxes et helpers communs.Assets/Src/Game/Data/ByModule/TeamModuleTypeData.cs— désérialisation du payload complet de bills.Assets/Src/UI/SimByModule/Data/Runtime/Bill.cs— objet runtime principal.Assets/Src/UI/SimByModule/Data/Runtime/BillDataBase.cs— factory dubillData.Assets/Src/UI/SimByModule/Data/Runtime/BillData*.cs— structures spécifiques.Assets/Src/UI/SimByModule/UI/BillWindowControl.cs— sélection du template et affichage/finalisation.Assets/Src/UI/SimByModule/UI/BillControlBase.cs— affichage commun, taxes/total, titres et raw data.Assets/Src/UI/SimByModule/UI/*BillControl.cs— contrôles spécialisés.Assets/Src/Game/Data/ByModule/ModuleSettingsBase.cs— settings runtime des bills.Assets/Src/UI/SimByModule/Data/BillsDataWizardPageControl.cs— configuration dans l'éditeur.
Configuration
Assets/GameSpecific/byModule/Resources/data_byModule/bills_settings_definitions.jsonAssets/GameSpecific/byModule/Resources/data_byModule/bills_category_settings_definitions.jsonAssets/GameSpecific/byModule/Resources/data_byModule/modules_settings_definitions.jsonAssets/GameSpecific/byModule/Resources/data_byModule/modules_settings.jsonpour les defaults applicables.accounts.json,taxes.json,interests.json,immobilizations.json,contract_regularizations.jsonlorsque le nouveau bill dépend de ces domaines.
10. Audit obligatoire après ajout
Après avoir ajouté le string du nouveau type, faire une recherche globale sur les endroits qui switchent sur les bill types. Le code actuel contient plusieurs décisions distribuées qui ne passent pas toutes par une factory unique.
git grep -n "new_bill" master/game master/node.js
git grep -n "BillType.eNewBill" master/game
git grep -n "switch.*billType|switch.*bill.billType" master/game master/node.js
Les hotspots les plus importants sont : taxes, sélection du partenaire fiscal, type simplifié/titre, mapping du template, génération d'entrées comptables, stock/inventaire, reports/régularisations et dépendances entre bills.
11. Test mental minimal d’un nouveau bill
flowchart TD
CFG[Le wizard peut le configurer ?] --> GEN[Le serveur le génère ?]
GEN --> SER[Il se serialize puis se deserialize ?]
SER --> UNITY[Unity reconnait son BillType ?]
UNITY --> DATA[CreateBillData retourne le bon data ?]
DATA --> UI[BillWindow choisit le bon template ?]
UI --> NUM[Totaux/taxes/entries corrects ?]
NUM --> STATE[Finished state et reload corrects ?]
STATE --> MULTI[Reload / autre client / contexte précédent correct ?]
Si une seule étape échoue, le nouveau bill n'est pas réellement intégré de bout en bout.
Inventaire et stocks
Modèle serveur
_utils/by_module/classes/InventoryProduct.js représente un produit d'inventaire. Il gère les entrées de stock, achats, ventes, méthode FIFO/non-FIFO et post-traitement selon le type de stock.
Fonctions structurantes
deserialize()/serialize();buyItem(quantity, price, billUid);sellItem(quantity, deleteEntry);addInventoryEntry(...);initializeInventoryProduct(...);postProcessInventoryEntries(...);getSellReport(quantity);- helpers de recherche/déduction par bill ou balance.
Modèle Unity
Assets/Src/UI/SimByModule/Data/Runtime/InventoryProduct.cs désérialise isFIFO, sellPrice, numDecimals et la liste InventoryEntries. Il expose notamment le prix de vente et le coût d'achat courant.
Flux
flowchart LR
STATIC[products / module settings] --> CREATE[Create Module Type Data]
CREATE --> INV[InventoryProduct]
BILL[Purchase/Sale Bills] --> INV
INV --> ENTRIES[Inventory entries]
ENTRIES --> COST[Cost of goods / stock]
COST --> REPORT[End Stock of Goods Report]
COST --> AK[Answer Key / accounts]
Permanent vs periodic stock
Les constantes By Module distinguent permanent_stock et periodic_stock. Les préférences de module et les traitements de fin de période déterminent comment l'inventaire influence les comptes et rapports.
Report de fin de stock
Le serveur possède EndStockOfGoodsReportBill, qui conserve les variations de stock. Côté Unity, BillDataEndStockOfGoodsReport représente ces données.
Tests
tests/unit/by_module_tests/classes_tests/inventory.test.js couvre la classe d'inventaire. Les changements qui touchent l'inventaire via les bills doivent aussi passer les tests bills + answer key.
Modifier une règle d’inventaire ou ajouter un comportement stock
Une règle de stock peut impacter génération, bills, account state, reports, Answer Key et Grading. Toujours tracer le cycle complet avant de modifier InventoryProduct.
Flux à respecter
flowchart LR
CFG[stock_type / settings] --> CREATE[createInventory]
CREATE --> INV[InventoryProduct]
BILLS[Purchase/Sale Bills] --> INV
INV --> COGS[Cost / balances]
COGS --> REPORT[EndStockOfGoodsReport]
COGS --> ACC[Account State]
ACC --> AK[Answer Key]
AK --> G[Grading]
Permanent vs periodic
stock_type distingue permanent_stock et periodic_stock. Ne pas ajouter une branche métier qui ignore ce setting : les comptes et la régularisation de fin de période ne se comportent pas de la même façon.
Ajouter/modifier une méthode de consommation
- Modifier
InventoryProduct.sellItem()/postProcessInventoryEntries()selon le besoin. - Vérifier FIFO/non-FIFO.
- Vérifier quantités partielles et stock insuffisant.
- Vérifier que
getSellReport()retourne encore les coûts attendus. - Mettre à jour les bills/reports qui consomment ces données.
- Vérifier l'account state et les écritures de stock.
- Ajouter des tests dans
classes_tests/inventory.test.js.
Cas limites à tester
- stock initial = 0;
- plusieurs achats à prix différents;
- vente traversant plusieurs lots FIFO;
- quantités décimales selon
numDecimals; - periodic stock avec régularisation de fin;
- contextes suivants qui reprennent l'inventaire précédent;
- reopen/restart module.
Immobilisations et amortissement
Responsabilité
Les immobilisations couvrent la génération des achats d'immobilisations, leur durée de vie, leur mode d'amortissement, les rapports d'immobilisations, les taxes associées et les écritures de régularisation.
Static data
La configuration principale se trouve dans immobilizations.json et les settings/modules qui activent les préférences liées aux immobilisations. La page Configuration Accounting — guide des JSON décrit la source de configuration.
Serveur
classes/bills/immobilizations/ImmobilizationsBill.js: bill d'acquisition;ImmobilizationsTaxesBill.js: taxes municipales/scolaires ou autre taxe configurée;reports/ImmobilizationsReportBill.js: rapport basé sur les immobilisations référencées;utilsByModule.getImmobilizationDepreciation(): calcul d'amortissement selon le type d'unité;BillCreationContext._usedImmobilizationsByType: contexte utilisé pendant la génération.
Modèles Unity
ImmobilizationData;SeededImmobilizationetSeededImmobilizationPlus;BillDataImmobilization;ImmobilizationBillEntry;ImmobilizationTaxeetImmobilizationTaxesBillEntry.
UI
Les rapports possèdent des contrôles dédiés : ImmobilizationsReportControl, variante Plus, rapports degressive/by usage et ImmobilizationsTaxesReportControl. Le contrôle détecte notamment le type d'amortissement pour adapter le contenu affiché.
flowchart LR
CFG[immobilizations.json] --> CREATE[Create Module Type Data]
CREATE --> SEED[SeededImmobilization]
SEED --> BILL[ImmobilizationsBill]
BILL --> ACC[Accounts / Journal]
SEED --> DEP[Depreciation calculation]
DEP --> REPORT[Immobilizations Report]
REPORT --> REG[Regularization entries]
Points d'attention
- Le type d'immobilisation détermine le compte impacté.
- Le calcul dépend des dates fiscales de l'activité.
- Les contextes suivants peuvent réutiliser les immobilisations précédentes.
- Un changement doit être testé dans création des données, answer key et grading.
Ajouter un type d’immobilisation ou une méthode d’amortissement
Nouveau type d'immobilisation
- Ajouter/configurer l'entrée dans
immobilizations.json. - Ajouter les comptes nécessaires dans le plan comptable si le type crée de nouveaux UIDs : actif, depreciation, cumulative depreciation, etc.
- Vérifier la génération dans Create Module Type Data et
ImmobilizationsBill. - Vérifier
ImmobilizationsReportBillet les données UnitySeededImmobilization*. - Vérifier le report UI correspondant.
- Vérifier les relations d'Answer Key et Grading qui construisent les écritures d'amortissement.
- Tester le passage au contexte suivant et la dépréciation cumulative.
Nouvelle méthode d'amortissement
Le calcul central est utilsByModule.getImmobilizationDepreciation(). Les branches existantes sont by_usage_km, linear et degressive.
- Ajouter le nouvel
unit_typedans le calcul central. - Définir précisément l'unité stockée dans
total_units_used. - Mettre à jour la propagation entre contextes dans Create Module Type Data.
- Adapter le report Unity s'il affiche des colonnes différentes.
- Vérifier Answer Key et Grading, qui appellent tous deux
getImmobilizationDepreciation(). - Tester acquisition avant, pendant et près de la fin de l'exercice.
Dates fiscales
Le calcul utilise actSettings.year, startFiscalDateObj et endFiscalDateObj. Un bug “un mois de trop/de moins” doit être reproduit avec les timestamps exacts et les bornes fiscales, pas uniquement avec le prix de l'actif.
Debug
Comparer dans l'ordre : seeded immobilization → bill entry → résultat de getImmobilizationDepreciation() → report → account relation → Answer Key → Grading. Cela localise rapidement si l'erreur vient de la génération ou de la correction.
Contrats
Responsabilité
Les contrats Accounting sont des données métier persistées/générées qui servent à créer des bills et rapports de contrat, puis à calculer les montants payés/reçus d'avance ou restant à payer/recevoir en fin de période.
Serveur
classes/bills/contracts/ContractBill.js: création du contrat/bill;classes/bills/reports/ContractsReportBill.js: rapport des contrats utilisés;BillCreationContext._usedContractsByType: registre des contrats générés;utilsByModule.getContractPaidOrReceivedInAdvance();utilsByModule.getContractToPayOrReceive().
Types de contrats
Le code traite notamment des contrats fournisseur, client, investissement et prêt court terme. Le type de contrat est stocké dans billData.contractType et influence taxes, comptes et sens de l'écriture.
Modèle Unity
SeededContract contient date, contractRegularizationData, liste de SeededContractBillEntry et unités. Il peut calculer son subtotal pour un type de bill donné.
Relation avec les produits/services
ContractBill réutilise les classes de bill data produit ou service pour stocker les entrées du contrat. Le contrat conserve aussi l'UID de régularisation correspondant.
flowchart LR
CFG[Contract config] --> CB[ContractBill]
CB --> USED[_usedContractsByType]
USED --> REPORT[ContractsReportBill]
USED --> REG[Contract Regularization]
REG --> GJ[Journal / Accounts]
Debug
Pour un montant de contrat incorrect, inspecter d'abord le bill et ses entries, puis contractType, les dates fiscales, contract_regularization_uid et enfin le calcul de régularisation.
Ajouter un type de contrat ou une règle de contrat
- Définir le type dans la configuration/static data concernée.
- Définir si le contrat représente fournisseur, client, investissement, prêt ou une nouvelle catégorie réellement différente.
- Vérifier
ContractBillet le type deBillDatautilisé pour ses entries. - Enregistrer le contrat dans
BillCreationContext._usedContractsByType. - Ajouter/adapter
ContractsReportBill. - Définir le
contract_regularization_uidcorrespondant. - Vérifier les calculs
getContractPaidOrReceivedInAdvance()etgetContractToPayOrReceive(). - Mettre à jour les relations de comptes Answer Key/Grading.
- Ajouter le rendu/report Unity si les colonnes diffèrent.
Dates et durée
Les calculs utilisent la date du contrat, le nombre d'unités/années et les bornes fiscales. Tester un contrat :
- commencé avant l'exercice;
- commencé pendant l'exercice;
- qui se termine pendant l'exercice;
- qui continue après la fin de l'exercice;
- sans fin explicite lorsque le helper accepte
-1.
Debug
Si un contrat apparaît dans le report mais régularise mal, le problème est probablement après ContractBill. Si le contrat lui-même manque, commencer par la génération/bill factory et _usedContractsByType.
Régularisations de fin de période
Rôle
Les régularisations transforment les données accumulées pendant le module en écritures de fin de période. Elles concernent notamment amortissements, contrats, stocks et comptes explicitement configurés pour être régularisés.
Préférences structurantes
Les constantes By Module exposent plusieurs flags :
uses_regularizations;uses_new_regularizations;regularization_accounts_count;affected_regularizations_reports;uses_stock_regularizations;has_new_immobilizations;has_new_contracts.
Calcul générique
utilsByModule.getAmountToRegularize() calcule la portion à régulariser en fonction du montant, de la date d'acquisition/début, de la fin de vie/contrat et des bornes de l'exercice. Les helpers spécialisés contrats et immobilisations s'appuient sur cette logique temporelle.
Static data
contract_regularizations.json fournit les types de régularisation de contrats. D'autres comportements sont pilotés par les settings de modules/pages et les comptes définis dans les données Accounting.
Runtime Unity
RegularizationDataBase;RegularizationBillEntryBase;ContractRegularizationBillEntry;BillDataContractRegularizationsReport.
UI
ContractRegularizationsReportControl;ContractWithInterestRegularizationsReportControl;- rows spécialisées correspondantes.
flowchart TD
DATA[Immobilizations / Contracts / Stock] --> PERIOD[End of fiscal period]
PERIOD --> CALC[Regularization calculations]
CALC --> REPORT[Regularization Reports]
CALC --> ENTRY[Journal entries]
ENTRY --> LEDGER[Ledger]
LEDGER --> FS[Financial statements]
Pourquoi cette page est séparée
Une régularisation n'est ni “un contrat”, ni “une immobilisation”, ni “un bill” au sens architectural. C'est une étape transversale de fin de période qui consomme ces domaines et produit des écritures.
Ajouter une nouvelle régularisation
1. Identifier la source
Une régularisation doit venir d'une donnée métier précise : contrat, immobilisation, taxe, stock ou compte explicitement configuré. Ne pas créer une écriture “magique” directement dans l'état financier.
2. Définir le calcul
Réutiliser getAmountToRegularize() lorsqu'il s'agit de répartir un montant sur une période. Le helper prend acquisition, fin de vie éventuelle, exercice fiscal, précision et sens inAdvance.
3. Définir les comptes
Ajouter les relations debit/credit dans la couche Account State appropriée. L'ordre compte : getRegularizationsAccountsOrdersAndRelations() est exécuté avant certaines étapes de closing/états.
4. Ajouter le report si l'utilisateur doit voir/calculer la régularisation
Créer ou étendre la Bill/report data serveur, le modèle Unity et le contrôle UI. Configurer la page dans les JSON.
5. Answer Key + Grading
Le même calcul de montant doit être utilisé des deux côtés. Le grading possède des branches pouvant utiliser les régularisations saisies par l'étudiant (useUserRegularizations) : vérifier si la nouvelle règle appartient à ce chemin.
Test minimal
- montant entièrement dans l'exercice;
- prorata début/fin d'exercice;
- in advance vs to pay/receive;
- arrondi selon
numDecimals; - impact trial balance;
- impact income statement/balance sheet;
- context suivant.
Answer Key — génération des bonnes réponses
Rôle
byModule_utils_answer_key_module.js construit les décisions correctes complètes d'un module. Ce n'est pas une simple liste de réponses : le moteur reconstruit les livres, journaux, états, formulaires et rapports nécessaires pour obtenir l'état comptable attendu.
Pipeline
flowchart TD
MTD[Module Type Data] --> INIT[init answer key]
INIT --> PAGE[compute each module page]
PAGE --> AJ[Auxiliary journals]
PAGE --> GJ[General Journal]
AJ --> STATE[Account state]
GJ --> STATE
STATE --> LED[General Ledger]
LED --> TB[Trial Balance]
TB --> FS[Financial Statements]
PAGE --> FORMS[Tax / payroll / bank forms]
PAGE --> REG[Regularizations]
FS --> OUT[pagesData + dropdowns + finished bills]
FORMS --> OUT
REG --> OUT
Calculs couverts
Le moteur contient des calculateurs spécialisés pour achats, ventes, encaissements, décaissements, payroll, petty cash, formulaires de taxes employé/employeur, subledgers fournisseur/client/stock, general journal, ledger, bank statements, trial balance, income statement, retained earnings/equity, balance sheet, accounting journal et bank reconciliation.
Ordre des dépendances
L'ordre est critique : les journaux alimentent l'état des comptes, qui alimente ledger/trial balance, qui alimente les états financiers. Une modification d'un calcul “local” peut donc modifier plusieurs pages suivantes.
Dropdowns dynamiques
L'answer key produit aussi des dynamicDropdownsData lorsque les options dépendent des données générées (partenaires, documents, relations ou lignes existantes).
Tests
answer_key_module.test.js;answer_key_module_functions.test.js;petty_cash_answer_key_module.test.js.
Debug
Dans server_scripts_debugging.js, testGeneralFunctionnalities() enchaîne création du Module Type Data → answer key → grading → reassemble. C'est le test manuel le plus direct lorsqu'un module complet ne grade plus à 100 % avec ses bonnes réponses.
Index détaillé des calculateurs Answer Key
byModule_utils_answer_key_module.js est un moteur de plusieurs milliers de lignes. Il faut le lire par type de page, pas du haut vers le bas.
Orchestration
byModuleAnswerKeyModule()— entrée publique;answerKeyModule()— orchestration;initAnswerKeyModuleVariables()— contexte/settings/pages;computeAnswerKeyModule()— boucle globale;computeAnswerKeyModulePage(modulePageUid)— routeur par page.
Journaux
computeCorrectPurchasesJournal();computeCorrectSalesJournal();computeCorrectCashingsJournal();computeCorrectWithdrawalsJournal();computeCorrectPayrollJournal();computeCorrectPettyCashJournal();computeCorrectGeneralJournal().
Subledgers / ledger / banque
computeCorrectProvidersSubledger();computeCorrectClientsSubledger();computeCorrectStocksSubledger();computeCorrectLedger();computeCorrectLedgerAutoReport();computeCorrectLedgerForBankReconciliation();computeCorrectBankStatement*;computeCorrectBankReconciliation().
États financiers
computeCorrectTrialBalance();computeCorrectStatementIncome()/ Services;computeCorrectStatementRetainedEarnings();computeCorrectStatementEquityPartnership();computeCorrectStatementEquity();computeCorrectBalanceSheet();computeCorrectAccountingJournal().
Ajouter une nouvelle page à l'Answer Key
- Ajouter/configurer la page dans les JSON et l'UI — voir Configuration Accounting et UI data-driven.
- Ajouter le
modulePageUidau routeurcomputeAnswerKeyModulePage(). - Créer une fonction
computeCorrect...qui lit uniquement les données dont elle dépend. - Construire la réponse via les helpers
getOrCreateTableData*, section/row/input plutôt qu'en réinventant le format. - Pour une page dérivée des comptes, utiliser l'account state partagé.
- Pour une page basée sur des bills, réutiliser
addBillAnswers(). - Ajouter les options/dynamic dropdowns si la correction en dépend.
- Ajouter le grading symétrique dans
byModule_utils_grade_module.js. - Ajouter un test Answer Key + un test grading.
Account State — cœur transversal
Trial balance et états financiers dépendent de l'état des comptes, lui-même construit en appliquant les relations dans un ordre précis :
getFormsAccountsRelations();getRegularizationsAccountsOrdersAndRelations();getClosingAccountsOrdersAndRelations();getAccountStatesOrdersAndRelations();updateAccount()/updateAccountsState().
Si plusieurs pages financières deviennent fausses en même temps, debugger l'account state avant chaque page individuelle.
Guide symptôme → zone
| Symptôme | Commencer ici |
|---|---|
| Achats/ventes faux | Aux journal + bill answers. |
| Journal correct, ledger faux | computeCorrectLedger* / reconstruction ledger. |
| Trial balance fausse | Account state + computeCorrectTrialBalance. |
| Income statement / balance sheet faux ensemble | Account state / closing / regularizations. |
| Bank reconciliation fausse | Bank statement + ledger BR + computeCorrectBankReconciliation. |
| Row/dropdown manquant | Builders structure + dynamic dropdowns + bill answers. |
Validation forte
Une Answer Key correcte doit, lorsqu'elle est utilisée comme décisions étudiant, grader à 100 % sur toutes les pages évaluées. testGeneralFunctionnalities() exécute exactement ce chemin et doit être utilisé comme test de cohérence globale après une modification importante.
Chaîne comptable — journaux, ledger et états financiers
Vue d'ensemble
Le moteur Accounting est construit en chaîne. Les journaux représentent les écritures source, le ledger est dérivé de ces journaux, puis la trial balance et les états financiers consomment les soldes produits.
flowchart LR
B[Bills / Forms] --> J[General + Auxiliary Journals]
J --> L[General Ledger]
L --> TB[Trial Balance]
TB --> IS[Income Statement]
TB --> EQ[Statement of Equity / Retained Earnings]
TB --> BS[Balance Sheet]
J --> BR[Bank Reconciliation]
Synchronisation automatique du ledger
utilsByModule.shouldSyncLedger() active la synchronisation lorsque la préférence AUTO_REPORT_TO_LEDGER est active et que la page modifiée fait partie des sources reconnues.
Les pages sources incluent notamment :
general_journal;purchases_journal;sales_journal;withdrawals_journal;cashings_journal;payroll_journal.
rebuildLedgerFromJournals()
Le serveur copie d'abord le Module Type Data et évalue les formules, efface uniquement la page dérivée ledger, puis rejoue les écritures des journaux en respectant sectionsIndex et rowsIndex. Les journaux source ne sont pas modifiés.
Les écritures du general journal et des journaux auxiliaires passent par des extracteurs différents avant d'être regroupées dans le ledger.
Quand le ledger est reconstruit
Les custom commands de sauvegarde de valeur, options, formule, suppression/réordonnancement de row/section vérifient si la page impacte le ledger. Si oui, elles reconstruisent le ledger puis envoient un message RTT spécialisé.
Answer Key
L'Answer Key utilise la même logique de reconstruction afin que la version correcte du ledger suive les mêmes règles que le ledger de l'étudiant.
UI Unity
Les contrôles spécialisés incluent notamment TrialBalanceControl, StatementIncomeControl, StatementEquityControl, BalanceSheetControl et BankReconciliationControl.
Debug d'une incohérence
- Vérifier la bill/form source.
- Vérifier l'écriture générée dans le journal.
- Vérifier si
AUTO_REPORT_TO_LEDGERest actif. - Inspecter le ledger reconstruit.
- Comparer la trial balance.
- Seulement ensuite inspecter l'état financier final.
Ajouter une nouvelle source comptable dans la chaîne
Une nouvelle opération comptable doit être branchée à l'endroit le plus en amont possible. Si elle produit une écriture, le ledger et les états doivent la recevoir via la chaîne existante plutôt que via un patch direct sur l'état financier.
Nouvelle écriture dans un journal
- Définir la source : bill, form, régularisation ou saisie étudiant.
- Définir debit/credit + comptes.
- Ajouter la réponse attendue dans l'Answer Key du journal.
- Ajouter le grading de l'écriture.
- Si
AUTO_REPORT_TO_LEDGERdoit l'inclure, vérifiershouldSyncLedger()et l'extracteur utilisé parrebuildLedgerFromJournals(). - Vérifier trial balance et états financiers sans code spécial additionnel.
Ne pas patcher le ledger à deux endroits
Le ledger auto-report est une donnée dérivée. Une custom command modifie le journal puis appelle le rebuild. Ajouter en parallèle une mutation manuelle du ledger crée des doublons et des divergences entre Answer Key et décisions étudiant.
Diagnostic par invariants
| Invariant cassé | Zone à inspecter |
|---|---|
| Débit ≠ crédit dans l'écriture source | Bill/form/regularization. |
| Journal bon, ledger incomplet | shouldSyncLedger / extracteur / rebuild. |
| Ledger bon, trial balance fausse | Account state / signed values. |
| Trial balance bonne, Income Statement faux | Mapping des comptes vers état. |
| Income Statement bon, Balance Sheet faux | closing/equity/retained earnings. |
Grading, reassemble et calcul des scores
Trois étapes distinctes
La correction By Module n'est pas un seul calcul. Elle sépare : grading brut, réassemblage/pondération, puis publication du résultat.
flowchart LR
DEC[Décisions étudiant] --> GRADE[byModuleGradeModule]
AK[Answer Key] --> GRADE
GRADE --> RAW[scores bruts + errors + expectedValues + ignoredPageUids]
RAW --> RE[byModuleReassembleAnswer]
RE --> FINAL[scores finaux + errorScores]
FINAL --> GEN[GenerateTeamModuleResults]
byModule_utils_grade_module.js
byModuleGradeModule() initialise le contexte de grading et compare la donnée étudiante aux valeurs attendues. Le moteur route chaque modulePageUid vers son correcteur spécialisé et retourne :
scoresbruts;errorsstructurées;expectedValues;ignoredPageUids.
Si les formules sont actives, leurs valeurs peuvent être pré-calculées avant la comparaison.
byModule_utils_reassemble_answers.js
byModuleReassembleAnswer() transforme les scores bruts selon la configuration de pages_settings. Il charge notamment inputs_ponderation, scores_ponderation, scores_details et les colonnes ignorées.
Le moteur supporte des chaînes de pondération et des pénalités. Il recalcule aussi un arbre errorScores qui permet au frontend d'associer la perte de points aux cellules/rows concernées.
Pages ignorées
Une page dans ignoredPageUids reçoit un score -1 au réassemblage. Cela distingue une page non évaluée d'une page évaluée à 0.
Arrondis
Le réassemblage contient une logique explicite de correction des erreurs d'arrondi autour de 0 afin que la somme des pénalités ne produise pas des scores légèrement négatifs ou des reliquats artificiels.
Configuration
Les pondérations sont data-driven. Modifier un score n'implique donc pas nécessairement de changer le JavaScript : commencer par vérifier pages_settings.json et ses scores_details/scores_ponderation.
Tests
Les tests de grade_module_tests/ couvrent grading générique, balance sheet, income taxes et petty cash. Lorsqu'un changement d'answer key modifie une valeur attendue, exécuter également les tests de grading correspondants.
Index détaillé du moteur de grading
byModule_utils_grade_module.js est encore plus volumineux que l'Answer Key. Il combine routeurs de pages, matching structurel et scoring générique des inputs/options.
Orchestration
byModuleGradeModule()— entrée publique;gradeModule()— orchestration;initGradeModuleVariables()— contexte de correction;trimData()— normalisation;preComputeFormulaValues()— calcul des formules avant comparaison;computeModulePageScore()— routeur de page.
Correcteurs spécialisés
Le moteur possède des fonctions distinctes pour purchases/sales/withdrawals/cashings/payroll/petty cash/general journal, taxes forms, subledgers, ledger, bank reconciliation, trial balance, statements et accounting journal. Lorsqu'une seule page échoue, commencer par son correcteur spécialisé avant les helpers génériques.
Matching structurel : pourquoi le grading n'est pas une simple égalité JSON
Le General Journal et plusieurs tableaux doivent accepter des rows dans un ordre ou une structure qui peut différer de l'Answer Key. Le moteur utilise notamment :
getMatchRateOfTwoAccountsAndImpactTypes();compareAccounts();getMatchRateForGeneralJournalSection();getTypeAndExplanationOfSection();billBelongsInGeneralJournal().
Avant de “simplifier” ce code en comparaison directe, vérifier les cas où l'étudiant peut produire une écriture équivalente mais ordonnée différemment.
Scoring générique des inputs
computeInputsGoodAnswers()prépare les valeurs attendues;inputHasGoodAnswer()applique valeurs/tolérances;computeRowInputsValueScore(), section/table/page remontent le score;computeInputsOptionsScore()corrige les options de format/choix;addError(),addExpectedValue(),addAnswerScore()alimentent les sorties.
Ajouter le grading d'une nouvelle page
- Déterminer si la page peut utiliser le grading générique table/inputs.
- Sinon créer
compute<Page>Score(). - Brancher la page dans
computeModulePageScore(). - Réutiliser les helpers de score/tolérance/options.
- Ajouter les
expectedValuesutiles à la correction visuelle. - Ajouter les erreurs avec un chemin suffisamment précis pour retrouver la cellule.
- Vérifier les
ignored_grading_*preferences. - Ajouter le score dans
pages_settings/scores_detailssi nécessaire. - Tester une bonne réponse = 100 %, puis plusieurs mauvaises réponses indépendantes.
Account State côté grading
Le grading reconstruit lui aussi l'état des comptes. Certaines fonctions reçoivent useUserRegularizations : cela signifie que la page doit parfois être évaluée à partir des régularisations effectivement saisies par l'étudiant plutôt qu'à partir de la bonne réponse. Ce détail est critique pour les pages dépendantes.
Grading vs Reassemble : frontière à respecter
| Problème | Modifier |
|---|---|
| Une réponse est reconnue bonne/mauvaise incorrectement | grade_module. |
| La bonne cellule perd le mauvais nombre de points | grading ou score detail selon le raw score. |
| Les raw scores sont bons mais le total final est faux | reassemble_answers / pondérations. |
| Une page ne devrait pas être évaluée | ignored pages/settings. |
Test de régression minimal
Pour chaque bug de grading : ajouter un test qui reproduit exactement la structure étudiante fautive, vérifier le score attendu, les errors et les expectedValues. Ensuite lancer la suite complète du type de page pour éviter de casser les équivalences existantes.
Matching centralisé et ledger — septembre 2026
matchUidsToAnswers() centralise la priorité des identifiants stricts puis les passages fuzzy jusqu’au seuil inclusif de 0.5 pour les journaux auxiliaires, la petite caisse, les subledgers clients/fournisseurs et les écritures admissibles du ledger. Le score de matching sélectionne une réponse attendue; il ne remplace pas le scoring des cellules. Le General Journal conserve son algorithme distinct.
Voir Fuzzy matching — logique de correspondance du grading Accounting pour les poids, seuils, exemples, limites et le changement du ledger du 30 septembre.
Résultats, évaluations et correction de module
Génération des résultats
byModuleGenerateTeamModuleResults() orchestre grading + reassemble puis persiste le résultat dans plusieurs documents.
sequenceDiagram
participant G as GenerateResults
participant TM as teamsModules
participant T as teams
participant E as evaluationTeam
participant RTT as RTT
G->>TM: lire moduleTypesData
G->>G: gradeModule()
G->>G: reassembleAnswer()
G->>T: teamDecisionsData.results[actModuleUid][moduleType]
G->>E: actPerf[actModuleUid][moduleType]
G->>RTT: execute_request GetTeamModuleTypeResults
RTT-->>Unity: récupérer le résultat
Document teams
Le résultat complet est enregistré dans teamDecisionsData.results[actModuleUid][moduleType]. Il contient notamment scores, errors, expectedValues et errorScores.
evaluationTeam
Les scores sont aussi propagés dans evaluationTeam.actPerf, afin d'alimenter l'évaluation globale de l'activité.
Summative retry
Lorsque la préférence RETRY_SUMMATIVE est active, le code compare la nouvelle note globale à lastSummativeGrade. La meilleure note est conservée; sinon les anciens scores d'évaluation sont restaurés. Un message module_graded est envoyé pour synchroniser l'état.
Pourquoi les résultats ne sont pas envoyés directement en RTT
Le commentaire serveur indique que le message serait trop gros. Le serveur envoie donc execute_request avec GetTeamModuleTypeResults; le client fait ensuite une commande dédiée pour récupérer la payload.
Correction côté Unity
Les résultats alimentent notamment CorrectedTeamModuleWindowControl, ModuleErrorsPopupControl, ModuleResultsReportItemControl et l'évaluation By Module. ignoreErrorFromTeamModuleResults permet de modifier l'état de certaines erreurs après correction.
Debug
Si l'answer key est correcte mais la note est mauvaise, inspecter dans l'ordre : résultat brut de gradeModule → reassembleAnswer → persistance dans teams → evaluationTeam → récupération côté Unity.
Ajouter une nouvelle donnée de résultat ou de correction
- Déterminer si la donnée appartient au raw grading, au reassemble ou au résultat final UI.
- Ajouter la valeur au bon objet retourné par grading/reassemble.
- Persister uniquement ce qui doit survivre au reload dans
teamDecisionsData.results[actModuleUid][moduleType]. - Si la valeur contribue à la note globale, mettre à jour
evaluationTeam.actPerf. - Ajouter la désérialisation Unity dans
ResultsByModuleou le modèle concerné. - Afficher la donnée dans la correction/report sans recalculer une deuxième logique différente côté Unity.
- Si la payload devient grosse, conserver le pattern
execute_request → GetTeamModuleTypeResults.
Retry summative
Toute nouvelle valeur de score doit être compatible avec le mécanisme RETRY_SUMMATIVE. Si la nouvelle tentative est moins bonne, le code peut restaurer l'ancienne note. Vérifier que les données auxiliaires de correction restent cohérentes avec la tentative retenue.
Debug du pipeline résultat
- inspecter la sortie
byModuleGradeModule(); - inspecter
byModuleReassembleAnswer(); - inspecter l'objet écrit dans
teams; - inspecter
evaluationTeam; - appeler manuellement
GetTeamModuleTypeResults; - inspecter la désérialisation Unity.
Procédure annuel pour updater les taxes (updated version 2026)
Note: La procédure assume que les liens utilisés resteront les mêmes et que la structure des fichiers PDF ne changera pas. Si les liens sont modifiés ou si la structure des PDF change, le script devra être ajusté en conséquence.
S.V.P Veuillez tout lire avant de faire la procédure
La mise à jour annuelle est maintenant automatisée avec le script :
node.js/exports/updateAnnualTaxData.js
Le script utilise les sources suivantes :
- CRA / ARC T4032 pour les tables fédérales et provinciales ON, NB, PE, QC
- Revenu Québec pour les tables provinciales QC, RRQ, RQAP
- CRA / Revenu Québec pour les taux CPP, EI, GST/HST, QST
Les sources et valeurs configurées sont dans :
node.js/exports/annualTaxSources.json
Comment ça fonctionne:
- Copier manuellement le Google Sheet income_taxes_<année précédente>.
- Renommer la copie en changeant l'année précédente par l'année en cours
- Copier l’ID du nouveau Google Sheet. ( L’ID est la partie dans l’URL entre /d/ et /edit.)
Exemple: https://docs.google.com/spreadsheets/d/190fSZq-r3K65Fb_g1D0oTQQZv5ijtIMjXvthD8MlhwM/edit - Lancer le self-test :
node exports/updateAnnualTaxData.js --year 2026 --templateYear 2025 --mode full --selfTest
Ce test vérifie :
- que les PDFs officiels sont accessibles
- que les tables sont parsées correctement
- que chaque table commence avec la ligne min_salary = 0
- que les salaires minimums sont en ordre croissant
- que les tables Québec sont mappées correctement (cas spécial)
- que les zéros sont exportés au bon format pour Google Sheets
- Lancer un dry run :
node exports/updateAnnualTaxData.js --year 2026 --templateYear 2025 --mode full --dryRun --incomeTaxesSpreadsheetId <SPREADSHEET_ID>
Le dry run ne modifie rien. Il affiche :
- les sources utilisées
- les fichiers qui seraient changés
- les onglets Google Sheets qui seraient modifiés
- les PDFs qui seraient uploadés
- les changements de taux entre l’année précédente et la nouvelle année
- Lancer l’export réel :
node exports/updateAnnualTaxData.js --year 2026 --templateYear 2025 --mode full --apply --incomeTaxesSpreadsheetId <SPREADSHEET_ID>
Utilisation Dans VS Code
Les configurations mentionnées ci-dessus existent dans .vscode/launch.json :
Annual Tax Export - Self Test
Annual Tax Export - Dry Run
Annual Tax Export - Run
Lors du lancement, VS Code demande :
-
taxYear: l’année à générer, ex. 2026 -
templateTaxYear: l’année précédente à utiliser comme modèle, ex. 2025 -
incomeTaxesSpreadsheetId: l’ID du Google Sheet copié manuellement -
annualTaxTargets: les destinations à mettre à jour -
setDefaultYear: si on veut changer l'année en cours ou non
Choix Des Destinations
Le script permet de relancer seulement une partie du processus avec --targets.
Options disponibles :
all
incomeTaxes
byModule
do
incomeTaxes,byModule
incomeTaxes,do
byModule,do
Signification :
incomeTaxes
Met à jour le Google Sheet copié income_taxes_<année> :
- écrit les onglets de tables d’impôt
- renomme les tabs de l’année précédente vers la nouvelle année
- met à jour exports/exportAllData.js avec le nouvel ID de sheet et les gids des tabs
byModule
Met à jour les colonnes de la nouvelle année dans le Google Sheet principal :
- taxes
- income_taxes
- employer_income_taxes
- interests
do
Upload les PDFs officiels dans DigitalOcean Spaces :
acc1/<année>/<theme>_<lang>
Fichiers Modifiés Par Le Script
Selon les targets choisis, le script peut modifier :
game/Assets/GameSpecific/byModule/Resources/data_byModule/income_tax_*_<année>.json
game/Assets/GameSpecific/byModule/Resources/data_byModule/taxes.json
game/Assets/GameSpecific/byModule/Resources/data_byModule/income_taxes.json
game/Assets/GameSpecific/byModule/Resources/data_byModule/employer_income_taxes.json
game/Assets/GameSpecific/byModule/Resources/data_byModule/interests.json
node.js/exports/exportAllData.js
Si --setDefaultYear est utilisé, il modifie aussi :
game/Assets/GameSpecific/byModule/Resources/data_byModule/act_settings.json
uniksimweb/uniksim-back-end/src/node.js/server/backend_braincloud.js
--setDefaultYear change l'année en cours utilisé dans la simulation.
Veuillez vous référer aux étapes ci-dessous si vous décider de ne pas changer l'année tout de suite.
- Modifier le champs year pour l'année par défaut voulu (
act_settings.json)
- Modifier l'année par défaut pour la création de sims démo (
backend_braincloud.js)
- Exporter les scripts acc1/acc2 et income_taxes_*
Finalement, il faut incrémenter la version dans version.txt et build pour que la nouvelle année en cours prenne effet.
Assets\Resources\data\version.txt
Diagramme qui montre comment le script fonctionne:
Contextes Accounting — cycle, héritage et données entre périodes
Rôle des contextes Accounting
Un contexte représente une période/situation comptable simulée avant le current_module. Il permet de construire l'état initial du module courant à partir d'une ou plusieurs périodes précédentes : bills, comptes, inventaire, immobilisations, contrats, pages calculées et dynamic dropdowns.
Les contextes ne sont donc pas une simple configuration visuelle. Ils font partie du pipeline serveur de génération du TeamModuleTypeData.
flowchart LR
MS[modules_settings.context_list] --> CTX[modules_contexts.json]
CTX --> MERGE[getContextData]
MERGE --> DATA[Copy current ModuleTypeData]
DATA --> BILLS[Create bills for context]
BILLS --> AK[Answer Key du contexte]
AK --> NEXT[getModuleTypeDataFromAnswerKeyModule]
NEXT --> STATE[Accounts / inventory / immobilizations / contracts]
AK --> SAVE[Saved pages + dropdowns]
BILLS --> PREV[previousContextsBills]
STATE --> CURRENT[Generate current_module]
PREV --> CURRENT
SAVE --> TEAM[TeamModuleTypeData]
CURRENT --> TEAM
Les deux fichiers de configuration à ne pas confondre
| Fichier | Responsabilité |
|---|---|
modules_settings.json |
Choisit quels contextes un module utilise, dans quel ordre, avec quelles dates et quels overrides. |
modules_contexts.json |
Définit les templates réutilisables de contexte : bills à générer, pages disponibles et configuration partagée. |
context_list dans modules_settings.json
Chaque module peut avoir une context_list. Un élément ressemble conceptuellement à :
{
"uid": "last_year",
"context_uid": "module_context_aux_journals_with_regularizations_last_year",
"start_date": {
"day": 1,
"month": 1,
"year_offset": -1
},
"end_date": {
"day": 31,
"month": 12,
"year_offset": -1
},
"preferences": {
"uses_regularizations": false
},
"saved_pages": [
"trial_balance_before_regularizations_and_closure"
]
}
| Champ | Rôle |
|---|---|
uid |
Identité de l'instance de contexte dans ce module. Sert notamment de clé dans previousContextsBills et les snapshots. |
context_uid |
Template chargé depuis modules_contexts.json. |
start_date / end_date |
Bornes utilisées pour générer les bills et calculs de cette période. year_offset permet de viser l'année précédente. |
preferences |
Overrides propres à ce contexte. Ils sont résolus via les helpers de préférence avec override. |
saved_pages |
Pages de l'Answer Key de ce contexte à conserver dans le TeamModuleTypeData final. |
modules_contexts.json : templates réutilisables
modules_contexts.json contient des définitions comme module_context_aux_journals_with_regularizations. Elles peuvent notamment fournir :
bills_type_data— types et quantités de bills à générer;pages_order— pages comptables utilisées par l'Answer Key du contexte;- des propriétés réutilisées par le moteur de création.
Héritage avec base_context_uid
getContextData(moduleContext) charge le template correspondant à moduleContext.context_uid. Si ce template possède base_context_uid, le moteur :
- copie le contexte de base;
- merge ensuite le contexte enfant par-dessus;
- applique ensuite les règles/preferences propres à l'instance du module.
Utiliser ce mécanisme lorsqu'un nouveau contexte est une variante d'un contexte existant plutôt que de dupliquer un énorme bloc bills_type_data.
Pipeline réel de création des contextes
Le cœur se trouve dans byModule_utils_create_module_type_data.js.
1. Initialiser l'état de départ
Le moteur possède un currentModuleTypeDataBase qui contient l'état comptable courant : comptes, inventaire, immobilisations, contrats, taxes, services/expenses, etc.
2. Parcourir context_list dans l'ordre
Pour chaque moduleContext :
getContextData(moduleContext)résout le template et ses overrides;- les dates absolues sont calculées depuis
actSettings.year; - le moteur fait un
deepCopyde l'état comptable courant; - les bills du contexte sont générés;
BillContext.getBills()devient le jeu de bills du contexte;- un Answer Key complet du contexte est calculé;
- les pages demandées par
saved_pagessont snapshotées; - le contexte est transformé en nouvel état comptable de base pour le contexte suivant.
3. Conserver les bills des contextes précédents
Après chaque contexte :
currentModuleTypeDataBase.previousContextsBills[moduleContext.uid] =
previousModuleTypeData.bills;
Les bills ne disparaissent donc pas après leur période. Ils restent disponibles pour les moteurs qui doivent consulter l'historique.
4. Générer le current_module
Une fois tous les contextes calculés, le moteur utilise l'état final comme point de départ du current_module. Les bills du module étudiant sont alors générés avec les comptes/inventaire/immobilisations/contracts issus des contextes.
BillCreationContext vs BillContext vs “module context”
Ces trois termes sont proches mais ne désignent pas la même chose.
| Concept | Responsabilité |
|---|---|
| Module context | Période logique définie dans context_list / modules_contexts.json. |
BillCreationContext |
État temporaire global utilisé pendant la création des bills : RNG, dates, partners, inventory, comptes, ordre de calcul, etc. |
BillContext |
Vue runtime des bills/comptes/inventory utilisée par les bills, Answer Key et calculs comptables après/pendant génération. |
Ne pas documenter ou coder ces trois concepts comme s'ils étaient interchangeables.
previousContextsBills
TeamModuleTypeData.previousContextsBills est indexé par contextUid. Chaque contexte contient typiquement :
billsData;billsOrder;partners;taxeslorsque présentes.
BillContext expose ensuite :
getPreviousContextsBills()— tous les bills historiques;getPreviousContextsBillsByType(...)— filtre par type;getPreviousContextsBillsByContextUid(contextUid);getBillByUid()— cherche d'abord dans le contexte courant, puis dans les contextes précédents.
Cas particulier module_context_client/provider
BillContext.getSerializedBillsBase() exclut actuellement les bills dont le type contient module_context_client lors de certaines sérialisations. Le code contient d'ailleurs un TODO explicite sur ce cas. Avant de modifier ces types, inspecter leur lifecycle complet au lieu de supposer qu'ils se comportent comme un bill standard.
savedPagesDataByContextUids
Si un contexte déclare saved_pages, le moteur récupère les pages correspondantes depuis l'Answer Key de ce contexte :
savedPagesDataByContextUids
└── <moduleContext.uid>
├── trial_balance_before_regularizations_and_closure
├── statement_income
└── ...
Côté Unity, TeamByModuleController.GetContextSavedPageData(actModuleUid, moduleContextUid, modulePageUid) permet de relire ces snapshots.
Quand utiliser saved_pages
Utiliser saved_pages lorsqu'une page historique doit être consultable/référencée plus tard. Ne pas l'utiliser pour recopier toutes les pages “au cas où” : chaque snapshot augmente les données persistées dans teamsModules.
savedDynamicDropdownsDataByContextUids
Lorsqu'une saved page dépend d'un dynamic dropdown, le moteur conserve aussi les entrées correspondantes dans :
savedDynamicDropdownsDataByContextUids[contextUid]
Sinon la page historique pourrait contenir des tables indexées par des UIDs que le client ne saurait plus nommer/afficher.
Propagation entre contextes
getModuleTypeDataFromAnswerKeyModule() transforme la sortie du contexte en état de départ pour le suivant.
| Donnée | Comportement entre contextes |
|---|---|
| Comptes | Recalculés depuis l'Answer Key / account state puis propagés. |
| Inventaire | Copié/transformé à partir de l'état final de BillContext. |
| Immobilisations | Copiées avec mise à jour de l'usage/dépréciation lorsque les règles du contexte l'exigent. |
| Contrats | Copiés vers le contexte suivant. |
| Taxes immobilisations | Copiées vers le contexte suivant. |
| Services / expenses | Conservés lorsqu'ils existent. |
| Bills | Archivé dans previousContextsBills; le contexte suivant obtient ses propres bills. |
Overrides de préférences par contexte
Un contexte peut modifier temporairement des préférences du module. Le code utilise getPreferenceWithOverride(moduleSettings, moduleContext, preference) pour plusieurs règles.
Exemples visibles dans modules_settings.json :
answer_key_until;uses_accounting_closing;uses_regularizations;uses_stock_regularizations;uses_taxes_payment_form;has_new_immobilizations;has_new_contracts.
Une préférence mise dans le contexte doit affecter uniquement cette période de génération. Si le comportement doit aussi changer dans le current module, configurer la preference au bon niveau du module.
initial_pages_data et préfixe previous_
Après les contextes, current_module.initial_pages_data peut construire l'état initial de certaines pages depuis l'état actuel ou précédent.
Le moteur traite notamment :
ledger/previous_ledger;bank_statement/previous_bank_statement;clients_subledger;providers_subledger;stocks_subledger;- fallback vers une page de l'Answer Key précédent.
Le préfixe previous_ indique au générateur de prendre la donnée du contexte/module précédent puis le préfixe est retiré avant de sauvegarder dans initialPagesData.
Ajouter un nouveau contexte de A à Z
- Décider s'il faut vraiment un nouveau template. Si seule une préférence/date change, réutiliser un
context_uidexistant avec overrides. - Si nécessaire, créer le template dans
modules_contexts.json. - Préférer
base_context_uidsi le nouveau contexte est une variante d'un gros contexte existant. - Ajouter l'instance dans
modules_settings.context_listau bon ordre. - Donner un
uidstable à l'instance. - Définir
start_date/end_dateetyear_offset. - Ajouter les overrides
preferencesnécessaires. - Ajouter seulement les
saved_pagesqui doivent réellement survivre. - Importer la static data Google Sheets/JSON si la source officielle passe par le pipeline d'export.
- Générer le Module Type Data avec un seed fixe.
- Inspecter l'état avant/après ce contexte.
- Vérifier que le contexte suivant et le current module partent du bon état.
Ajouter une nouvelle donnée qui doit traverser les contextes
C'est une modification plus profonde. Il faut auditer au minimum :
createInitialModuleTypeData()— création initiale;createBills()/BillCreationContext— utilisation pendant la période;BillContext— état calculé;getModuleTypeDataFromAnswerKeyModule()— propagation vers le contexte suivant;TeamModuleTypeData.cs— désérialisation Unity;- migration
teamsModulessi vieux documents; - Answer Key et Grading si la donnée influence une page comptable.
Debug d'un contexte
Pour un bug “la bonne valeur existe dans l'année précédente mais disparaît dans le module courant”, inspecter dans cet ordre :
- Configuration — le contexte est-il réellement dans
context_list? - Template —
context_uidexiste-t-il dansmodules_contexts.json? - Merge —
base_context_uid+ overrides produisent-ils le contexte attendu ? - Dates — vérifier
year_offsetet les timestamps calculés. - Bills du contexte — inspecter
moduleTypeDataForContext.bills. - Answer Key du contexte — la valeur attendue existe-t-elle déjà ici ?
- Transformation — inspecter
getModuleTypeDataFromAnswerKeyModule(). - previousContextsBills — le contexte est-il présent sous le bon
uid? - saved_pages — le snapshot est-il présent ?
- current_module — la donnée est-elle encore correcte juste avant sa génération ?
Test de régression recommandé
| Cas | Vérification |
|---|---|
| Contexte année précédente | Dates avec year_offset = -1, balances correctement reportées. |
| Deux contextes successifs | Le second part de l'état final du premier. |
saved_pages |
Page + dynamic dropdowns relisibles côté Unity. |
| Inventory | Stock final du contexte précédent devient l'état de départ suivant. |
| Immobilisations | Usage/dépréciation cumulative correcte. |
| Contrats | Contrat encore disponible et régularisation correcte. |
| Bills historiques | BillContext.getBillByUid() retrouve un bill précédent. |
| Current module | Answer Key final et grading à 100 % avec les données propagées. |
Fichiers clés
Assets/GameSpecific/byModule/Resources/data_byModule/modules_settings.jsonAssets/GameSpecific/byModule/Resources/data_byModule/modules_contexts.jsonnode.js/backend/braincloud/server_scripts/_utils/by_module/byModule_utils_create_module_type_data.js.../_utils/by_module/contexts/BillCreationContext.js.../_utils/by_module/contexts/BillContext.jsAssets/Src/Game/Data/ByModule/TeamModuleTypeData.csAssets/Src/UI/SimByModule/Controllers/TeamByModuleController.cs
Accounting 1 vs Accounting 2 — différences réelles
But de cette page
ACC1 et ACC2 partagent le même moteur By Module / Accounting, mais ils ne diffèrent pas seulement par le thème visuel. Plusieurs différences sont encodées directement dans les settings disponibles, les types d'entreprise, la forme juridique, les partenaires, les produits/services, les modules disponibles et certaines branches de l'Answer Key.
Cette page répond à deux questions :
- Qu'est-ce qui est réellement différent entre ACC1 et ACC2 ?
- Quand une modification doit-elle aller dans le commun, ACC1, ACC2 ou être testée dans les deux ?
Résumé fonctionnel
| Dimension | ACC1 | ACC2 |
|---|---|---|
| Univers métier | Entreprise spécialisée dans la vente de piscines. | Entreprise spécialisée dans la vente de café et de machines à café. |
| Objectif pédagogique | Compléter un cycle comptable. | Compléter un cycle comptable. |
| Framework technique | By Module + acc_base. |
By Module + acc_base. |
| Type d'activité de l'entreprise | our_company_type disponible : commerciale / services / mixte selon le module. |
Même système. |
| Forme juridique | Preference masquée; les defaults des modules sont essentiellement single_owner. |
Preference ACC2-only : société en nom collectif, société par actions A, société par actions A+B. PAS DE single_owner. |
| Créances / effets à recevoir | Non exposés dans le wizard. | has_debts et has_interests sont ACC2-only. |
| Petite caisse | Le moteur commun existe, mais le module n'est pas disponible dans le wizard ACC1. | module_petty_cash est ACC2-only. |
| Co-propriétaire / actionnaires | Pas d'entités game-specific correspondantes dans companies/partners. |
our_company_co_owner, shareholder_group_a, shareholder_group_b. |
| Companies | 66. | 67. |
| Partners | 76. | 77. |
| Products | 77. | 43. |
| Services | 41. | 58. |
| Tutoriels game-specific | Plusieurs scripts/prefabs ACC1 dédiés. | Pas d'équivalent dans le dossier ACC2 actuel. |
Attention : “Type d'entreprise” et “Forme juridique” sont deux settings différents
C'est important car les deux notions sont faciles à mélanger.
our_company_type — disponible dans le framework des deux jeux
Le setting our_company_type décrit l'activité économique de l'entreprise :
products_company— commerciale;services_company— de services;products_and_services_company— commerciale et de services.
Ce setting influence notamment :
- stock / inventaire;
- comptes visibles;
- types de bills disponibles;
- pages/relations liées aux ventes et services.
company_legal_form — ACC2-only dans le wizard
Dans preferences_settings_definitions.json :
"company_legal_form": {
"category": "company",
"type": "dropdown",
"data_uid": "company_legal_form",
"acc2_only": true
}
PreferencesWizardPageControl.ShouldSkipPreference() masque toute preference marquée acc2Only lorsque CurrentGameUid != "acc2".
Formes juridiques supportées par le moteur
Le moteur By Module connaît explicitement :
| UID | Libellé | Conséquence principale |
|---|---|---|
single_owner |
Propriétaire unique | Capital et retraits d'un seul propriétaire. |
partnership |
Société en nom collectif | Deux propriétaires; capital, apports/retraits et profits peuvent être répartis. |
joint_stock_a |
Société par actions — groupe A | Capital-actions A + bénéfices non répartis. |
joint_stock_ab |
Société par actions — groupes A et B | Capital-actions A+B + bénéfices non répartis; deux groupes d'actionnaires. |
ACC1 contient les textes/structures génériques nécessaires au moteur, mais la preference est explicitement masquée dans son wizard. ACC2 expose réellement cette variation au professeur.
Impact technique de la forme juridique
Ce n'est pas un simple label. company_legal_form traverse plusieurs moteurs.
1. BillCreationContext / BillContext
Les deux contextes lisent :
this._companyLegalForm =
utilsByModule.getModulePreference(
moduleSettings,
utilsByModule.constants.modulesPreferences.COMPANY_LEGAL_FORM
);
Pour une partnership, ourCompanyOwnerShares vaut par défaut 50 si aucune valeur n'est présente.
2. Répartition propriétaire / co-propriétaire
BillContext.getOwnerPaymentAmount() répartit un montant entre propriétaire/co-propriétaire lorsque la forme est :
partnership;joint_stock_abpour certains calculs actionnaires.
3. Génération de bills
BillCreationContext.getPayingOwnerUid() peut choisir un ou les deux propriétaires pour une partnership. Le BillFactory possède aussi des branches spécifiques pour :
- les paiements des propriétaires;
- les retraits;
- la Petite caisse;
- la création de plusieurs bills lorsque plusieurs propriétaires paient.
4. Comptes visibles
accounts.json possède des conditions de visibilité basées sur la forme juridique. Exemples :
- capital du propriétaire;
- capital du co-propriétaire;
- retraits/apports du co-propriétaire;
- capital-actions;
- bénéfices non répartis.
5. État des capitaux propres
L'Answer Key change réellement la structure :
- single owner → capital du propriétaire;
- partnership → capital propriétaire + co-propriétaire;
- joint stock → capital-actions + bénéfices non répartis;
- joint_stock_ab → deux groupes de capital-actions.
6. Clôture
Dans byModule_utils_answer_key_module.js :
- une partnership répartit le résultat entre
our_company_capitaletour_company_co_owner_capital; - une société par actions transfère le résultat vers
retained_earnings; - un propriétaire unique utilise le capital propriétaire.
Donc toute modification touchant capital, fermeture, equity, dividends, owner bills ou financial statements doit être testée avec plusieurs formes juridiques ACC2.
Différences ACC2-only encodées dans le framework partagé
Un grep de acc2_only dans la static data By Module montre actuellement ces différences explicites :
| Feature | Fichier | Effet |
|---|---|---|
company_legal_form |
preferences_settings_definitions.json |
Preference visible seulement dans ACC2. |
has_debts |
preferences_settings_definitions.json |
Créances activables seulement dans ACC2 via le wizard. |
has_interests |
preferences_settings_definitions.json |
Effets à recevoir / intérêts activables seulement dans ACC2 via le wizard. |
module_petty_cash |
modules_settings_definitions.json |
Module proposé uniquement dans ACC2. |
Filtrage du module Petite caisse
ModuleDataAndNameWizardPageControl vérifie moduleDefinition.acc2Only et ignore le module lorsqu'on n'est pas dans ACC2.
Filtrage des preferences
PreferencesWizardPageControl.ShouldSkipPreference() applique le même principe aux preferences ACC2-only.
ACC2 — Créances et effets à recevoir
Les preferences :
has_debts— libellé FR actuel : Créances;has_interests— libellé FR actuel : Effets à recevoir.
Elles sont référencées dans plusieurs définitions de modules : General Journal, Auxiliary Journals, Regularizations, Balance Sheet, Financial Statements, etc. Elles peuvent donc affecter :
- bills clients;
- radiation de client;
- provisions;
- notes à recevoir;
- intérêts;
- ledger;
- trial balance;
- financial statements;
- Answer Key / Grading.
Une modification de ces chemins doit être testée dans ACC2 avec les toggles ON et OFF.
ACC2 — Module Petite caisse
module_petty_cash est marqué acc2_only: true dans modules_settings_definitions.json.
Contenu game-specific ACC2
ACC2 possède notamment :
ModulePettyCashScenarioPages.asset;petty_cashcomme partenaire/contexte;- fournisseurs Petite caisse : convenience, maintenance, mail, florist, stationery;
petty_cash_initial_fund;- dépenses : lait, pain, papier hygiénique, timbres, enveloppes, colle, vis, ruban, fleurs, papeterie, etc.;
- retraits personnels;
- textes/références régionaux QC/ON/NB/PE.
Moteur commun malgré tout
Les classes techniques de Petite caisse sont dans le commun By Module, par exemple :
PettyCashDeclarationBill;PettyCashReplenishmentCheckBill;- branches Petty Cash du
BillFactory; - journal/Answer Key/Grading correspondants.
Autrement dit : le moteur est partagé, mais ACC2 est le jeu qui expose officiellement le module.
Différences de partenaires / companies
ACC1 — 66 companies
Les fournisseurs principaux sont liés aux piscines et à l'extérieur :
motor_company;pipe_company;aluminium_company;wood_company;chlore_company/chlore_salt_company;solar_canvas_company/winter_canvas_company;stairs_company;light_company;outdoor_company;toys_company;barbecue_company.
ACC2 — 67 companies
Les fournisseurs principaux sont liés au café :
coffee_company;espresso_company;beans_company;brew_company;blend_company;grind_company;roast_company;barista_company.
ACC2 ajoute en plus des entités structurelles absentes des companies ACC1 :
our_company_co_owner;shareholder_group_a;shareholder_group_b;petty_cash;- les fournisseurs de Petite caisse.
Partner subtypes différents
ACC2 possède des partner_sub_type supplémentaires :
our_company_co_owner;shareholder_group_a;shareholder_group_b;petty_cash;petty_cash_convenience_oc;petty_cash_maintenance_oc;petty_cash_mail_oc;petty_cash_florist_oc;petty_cash_stationery_oc.
Ces différences ne sont pas décoratives : BillFactory et plusieurs bill classes sélectionnent les partenaires selon leur subtype.
Produits : univers complètement différent
ACC1 — 77 products
Types et exemples :
- piscines :
above_pool_*,mid_pool_*,in_pool_*; - entretien : chlore, algicide, robot cleaner;
- extérieur : sofa, gazebo, toys, barbecue;
- composants de piscine : moteurs, tuyaux, aluminium, bois, chauffe-eau, systèmes chlore, toiles, escaliers, lumières;
- plus les produits techniques communs : deposit/withdrawals, bank, office supplies, loans, etc.
ACC2 — 43 products
Types et exemples :
- espresso machines : regular/highend/deluxe;
- coffee machines : regular/highend/deluxe;
- beans : raw/roasted/blends;
- retail/accessoires : brush, descaler, air blower, grinders, roasters, filters, scales, tampers, cups;
- plus les produits techniques communs.
Product types propres à ACC2
espresso_machine;coffee_machine;beans;our_company_co_owner;shareholder_group_a;shareholder_group_b.
Services : ACC2 en contient davantage
ACC1 — 41 services
Une grosse partie est liée aux piscines :
- fermeture de piscine par type/taille;
- nettoyage;
- maintenance;
- installation;
- installation de chauffe-eau;
- services/operating costs communs.
ACC2 — 58 services
ACC2 ajoute de nombreuses familles :
- Petite caisse : fonds initial, consommables, dépenses et retraits;
- formation barista : novice / advanced / expert;
- coffee shop : installation, cleaning, maintenance;
- maintenance machines : repair, cleaning, descaling;
- maintenance équipements : grinder sharpening, scale calibration, parts replacing;
- eau / steam wand;
- stocks/actionnaires.
Ce qui reste commun dans les datasets
Dans l'état actuel du dépôt, les différences les plus importantes ne viennent pas de :
conditions.json;employees.json;salaries.json.
Les différences sont surtout dans companies, partners, products, services, les ScenarioPages, les assets et les settings ACC2-only.
Server scripts ACC1 vs ACC2
Côté serveur, les wrappers restent volontairement presque identiques.
Commandes présentes dans les deux
answer_key_module;change_module_item_index;change_module_sort_index;change_module_visibility;create_team;custom_command;generate_new_good_decisions;generate_results;get_new_good_decisions;reopen_module;set_correction_mode_off.
acc1_utils.js vs acc2_utils.js
Ils sont actuellement identiques à part le GameUid et les noms de classes :
GameSpecificUtilsAcc1 -> GameSpecificUtilsAccBase("acc1")
GameSpecificUtilsAcc2 -> GameSpecificUtilsAccBase("acc2")
Donc une règle comptable commune doit aller dans acc_base ou _utils/by_module, pas être dupliquée dans les deux fichiers.
Exception historique ACC1
acc1_module_sort_name.js existe uniquement dans ACC1 et ne fait qu'inclure acc_base_change_module_sort_index. C'est un alias historique, pas une différence métier.
UI / scenes / skins
| ACC1 | ACC2 |
|---|---|
Acc1Student.unity |
Acc2Student.unity |
Acc1Teacher.unity |
Acc2Teacher.unity |
MainControlStudentAcc1.prefab |
MainControlStudentAcc2.prefab |
MainControlTeacherAcc1.prefab |
MainControlTeacherAcc2.prefab |
Acc1Skin.asset |
Acc2Skin.asset |
| Doozy graphs ACC1 | Doozy graphs ACC2 |
Tutoriels : différence ACC1 importante
ACC1 contient actuellement toute une couche game-specific absente d'ACC2 :
AccGeneralTutorials.cs;GenericAcc1Tutorial.cs;GenericAcc1TutorialNoModules.cs;TutorialButtonAcc1Async.cs;TutorialButtonAcc1Sync.cs;TutorialPopupControlAcc1Async.cs;TutorialPopupControlAcc1Sync.cs;- prefabs
TutorialPopupControlAcc1*.
Si une UI commune By Module est modifiée, tester que les UIDs ciblés par ces tutoriels ACC1 existent toujours.
ScenarioPages
Les deux jeux possèdent la majorité des mêmes ScenarioPages Accounting :
- Accounting Cycle;
- Accounting Journal;
- General Journal;
- Auxiliary Journals / Ledgers;
- Taxes;
- Income Taxes / Employer Income Taxes;
- Regularizations;
- Financial Statements;
- Closure;
- Balance Sheet.
ACC2 possède en plus ModulePettyCashScenarioPages.asset. Plusieurs assets homologues ont également des différences de contenu/références : ne pas copier un asset ACC1 sur ACC2 ou inversement sans diff.
Quand modifier où ?
| Changement | Emplacement attendu |
|---|---|
| Règle comptable valable pour ACC1 + ACC2 | acc_base / By Module commun. |
| Nouveau bill générique | Pipeline Bills commun. |
| Support d'une nouvelle forme juridique | Framework commun + settings, puis exposition selon le jeu. |
| Modification du comportement partnership/joint stock | BillContext/BillFactory/Answer Key/Grading communs; tester ACC2. |
| Nouvelle feature créances/intérêts | Framework commun, mais activation ACC2-only tant que le produit reste ainsi. |
| Nouveau contenu Petite caisse | ACC2 data; moteur commun si logique. |
| Nouveau fournisseur piscine | ACC1 static data. |
| Nouveau fournisseur café | ACC2 static data. |
| Changement visuel | GameSpecific concerné. |
| Changement tutoriel général ACC1 | GameSpecific ACC1. |
Matrice de test recommandée
Une feature Accounting qui touche une zone commune ne devrait pas être validée sur un seul jeu.
| Feature touchée | Tests minimum |
|---|---|
| Bills/journaux/ledger généraux | ACC1 + ACC2. |
| Inventory | ACC1 + ACC2 avec company type/stock type pertinent. |
| Capital/equity/closing | ACC1 default + ACC2 single owner + partnership + joint stock A + joint stock AB. |
| Owner bills | ACC1 owner unique + ACC2 partnership. |
| Dividends / retained earnings | ACC2 joint stock. |
| Créances / effets à recevoir | ACC2 ON/OFF. |
| Petite caisse | ACC2. |
| Tutoriel/UI modules | ACC1 + ACC2; vérifier ACC1 tutorial targets. |
| Scenario | Asset du même module dans les deux jeux. |
Fichiers clés pour comprendre les différences
Assets/GameSpecific/acc1/Resources/data_acc1/Assets/GameSpecific/acc2/Resources/data_acc2/Assets/GameSpecific/byModule/Resources/data_byModule/preferences_settings_definitions.jsonAssets/GameSpecific/byModule/Resources/data_byModule/modules_settings_definitions.jsonAssets/Src/UI/SimByModule/UI/Wizards/SaveModule/PreferencesWizardPageControl.csAssets/Src/UI/SimByModule/Data/ModuleDataAndNameWizardPageControl.csnode.js/backend/braincloud/server_scripts/_utils/by_module/contexts/BillCreationContext.jsnode.js/backend/braincloud/server_scripts/_utils/by_module/contexts/BillContext.jsnode.js/backend/braincloud/server_scripts/_utils/by_module/factories/BillFactory.jsnode.js/backend/braincloud/server_scripts/_utils/by_module/byModule_utils_answer_key_module.js
Pages liées
- Architecture By Module / Accounting
- Contextes Accounting
- Configuration Accounting — guide des JSON
- Pipeline des Bills
- acc_base, ACC1 et ACC2 — séparation des responsabilités
Fuzzy matching — logique de correspondance du grading Accounting
À quoi sert le fuzzy matching ?
Le grading doit retrouver quelle facture ou écriture attendue correspond à une ligne ou section étudiante, même si son identifiant manque ou est erroné. Le matching choisit cette correspondance; les correcteurs évaluent ensuite les valeurs, options, dates, références, soldes et l’ordre. Un match trouvé ne rend donc pas automatiquement la réponse correcte et son score de ressemblance n’est pas la note finale.
Cette logique est exécutée dans le script serveur BrainCloud node.js/backend/braincloud/server_scripts/_utils/by_module/byModule_utils_grade_module.js, à l’intérieur de byModuleGradeModule(). Voir Grading, reassemble et calcul des scores pour la pondération finale.
Le helper commun : matchUidsToAnswers
Signature : matchUidsToAnswers(uids, answersByKey, getStrictAnswerKey, getMatchScore). Il reçoit les UID étudiants ordonnés, les réponses candidates indexées par clé, une fonction d’identification stricte et une fonction de ressemblance. Il retourne uniquement answerKeyByUid; une entrée sans correspondance est absente de ce résultat.
flowchart TD
A[Entrées étudiantes et réponses candidates] --> B[Passage strict dans l'ordre des UID]
B --> C[Réserver chaque clé stricte une seule fois]
C --> D[Entrées et réponses restantes]
D --> E[Seuil initial 1]
E --> F[Meilleure réponse disponible au-dessus du seuil pour chaque entrée]
F --> G[Consommer la réponse et retirer l'entrée appariée]
G --> H{Au moins un match dans ce passage ?}
H -->|Oui| F
H -->|Non| I{Score restant au moins égal à 0.5 ?}
I -->|Oui| J[Abaisser le seuil au meilleur score observé]
J --> F
I -->|Non| K[Retourner la correspondance partielle]
1. Priorité stricte
Le passage strict traite toutes les entrées avant tout fuzzy matching. Une clé non nulle est retenue si elle n’a pas déjà été utilisée. Elle est retirée des réponses restantes. Une deuxième entrée demandant la même clé passe dans le lot fuzzy; elle ne peut pas réutiliser cette réponse, mais peut être associée à une autre.
La validation métier de la clé appartient au callback. getStrictBillUid lit bill_id, retrouve la facture dans _billsByNumber et vérifie getBillAffectedPageUid(). Un identifiant valide pour la page est prioritaire même si les montants ou la date ressemblent davantage à une autre facture; ces champs seront corrigés ensuite.
2. Passages fuzzy
- Commencer à
currentThreshold = 1. - Pour chaque UID restant, parcourir les réponses disponibles et retenir le plus grand score satisfaisant
score ≥ currentThreshold. - Consommer immédiatement la réponse retenue. Aucun retour arrière ne réaffecte un match déjà choisi.
- Lorsqu’un passage n’a produit aucun match, abaisser le seuil au plus grand score inférieur au seuil courant mais supérieur ou égal à
0.5. - Arrêter si aucune paire admissible ne reste, ou si l’un des lots est vide.
Le seuil descend vers les scores réellement observés : ce n’est pas une suite fixe 1 → 0,9 → 0,8. Une égalité conserve la première réponse rencontrée, car une nouvelle candidate doit satisfaire score > bestScore. Le résultat dépend donc de l’ordre des UID et des clés des réponses.
Qui utilise le helper commun ?
| Correcteur | Unité et priorité stricte | Score fuzzy |
|---|---|---|
computeAuxJournalScoreBase : achats, ventes, retraits, encaissements, paie |
Section; première row contenant bill_id → facture de cette page | calculateMatchScore : partenaire, mois/année, comptes et montants |
computePettyCashJournalScore |
Row; bill_id → facture de cette page | calculateRowMatchScore : mois/année et comptes/montants |
computeSubledgersJournalTableScore : clients/fournisseurs |
Row; clé construite table/partenaire + send/receive + bill_id, hors upfront_payment | calculateRowAmountMatchScore : mois/année, sens et total de la facture |
computeLedgerAccountTableScore |
Row admissible du compte; bill_id présent dans ses réponses attendues | calculateRowAmountMatchScore : mois/année, sens et montant de l’écriture de ce compte |
Le helper n’est pas branché sur tous les correcteurs du fichier. Les stocks et le General Journal possèdent d’autres règles.
Comment sont calculés les scores ?
Journaux auxiliaires : section
calculateMatchScore retourne 0 si la facture ne concerne pas cette page. Les poids sont partenaire 0.3, date 0.3, comptes 0.4. Le partenaire correspond à l’expéditeur ou au destinataire de la facture; la date compare le mois et l’année. Les poids disponibles sont additionnés au dénominateur.
Les comptes sont extraits des rows, en excluant date, partner_uid, bill_id, report_to_account, account_uid. Les colonnes débit/crédit sont rattachées à account_uid, avec un signe négatif pour le crédit. Pour un compte présent dans la facture, le code attribue une contribution de présence ou de sens d’impact, puis une contribution de proximité du montant. La proximité utilise max(0, 1 - abs(saisi - attendu) / attendu) lorsque l’attendu est non nul.
Détails de l’implémentation actuelle : le test de date utilise if (sectionMonth && sectionYear). Comme janvier vaut 0 dans Date.getMonth(), le facteur date est omis en janvier. Le cumul de contributions par compte peut aussi produire un score supérieur à 1 : il n’est pas borné. Il faut conserver ces détails lorsqu’on diagnostique un résultat, plutôt que supposer une probabilité normalisée.
Petite caisse : row et comptes
calculateRowMatchScore utilise date 0.33 et comptes 0.66. Les comptes renseignés sont comparés à ceux de la facture; date, partner_uid, bill_id, petty_cash_total sont exclus. Un compte absent de la facture contribue 0. Un montant attendu nul et saisi nul contribue 1; sinon le code utilise la proximité relative du montant. La contribution moyenne des comptes est pondérée par 0.66.
La date compare uniquement mois/année, avec un test de présence différent de celui des sections. Sans facteur disponible, le résultat est 0. Le partenaire et le total petite caisse ne servent pas au matching de cette row.
Subledgers et ledger : row et montant
calculateRowAmountMatchScore utilise date 0.33 et montant 0.66. Le sens saisi doit être le sens attendu; si débit et crédit sont tous deux renseignés, le débit est prioritaire. Pour un attendu non nul, la contribution montant est max(0, 1 - abs(saisi - attendu) / abs(attendu)). Les facteurs présents sont renormalisés par leur somme; sans facteur, le score vaut 0.
Une date présente mais différente reste dans le dénominateur avec une contribution nulle. Une date absente est exclue. Si seul le mois/année correspond et que le montant est incorrect, le score est environ 1/3 : insuffisant pour le seuil 0.5. Si la date est absente et le sens/montant sont corrects, le score peut atteindre 1.
Exemple chiffré
Écriture attendue : débit 100 en septembre. L’étudiant saisit débit 90 dans le même mois et la même année, sans identifiant strict valide.
proximité montant = 1 - |90 - 100| / 100 = 0.9
score = (0.33 × 1 + 0.66 × 0.9) / (0.33 + 0.66)
= 0.9333…
La row peut donc être appariée lors d’un passage fuzzy, si la réponse reste disponible. Le montant 90 est ensuite comparé à 100 dans le grading des cellules; cet appariement n’accorde pas 93,33 % de la note.
Ce qui a changé dans le ledger le 30 septembre
Le ledger construit les réponses attendues par compte et numéro de facture ou formulaire, avec sens, montant, page de référence et date. Le nouveau helper remplace le fallback local d’appariement et permet de retrouver une écriture à partir de son sens, montant et mois/année quand le bill_id ne permet pas de l’identifier.
Seules les rows ayant une reference participent à cet appariement. Avec les journaux auxiliaires actifs, la référence doit contenir general_journal; sans eux, toutes les rows portant une référence sont candidates. Les reports agrégés des journaux auxiliaires et les réponses de régularisation/clôture conservent leur traitement spécialisé. La recherche ne traverse pas tous les comptes : chaque table possède ses candidates.
Après matching, le correcteur prépare bill_id, reference, la date, le débit/crédit attendu, puis corrige le solde et l’ordre. Les réponses non rencontrées peuvent produire table_bill_missing ou table_form_missing. Une référence invalide peut produire row_reference_missing.
Le General Journal garde un algorithme distinct
computeGeneralJournalScore ne passe pas par matchUidsToAnswers. Il construit wantedExplanationBySectionUids pour factures, formulaires et relations de régularisation/clôture, avec ses propres passages de seuil, priorités et traitement des doublons.
getMatchRateForGeneralJournalSection pondère la ressemblance des comptes par 4, puis ajoute l’explication sélectionnée et la date exacte lorsqu’elles sont disponibles. Les factures/formulaires comparent aussi montants et sens; les relations peuvent comparer la structure des comptes sans leurs valeurs. Le plancher des passages est 0.4 (le code arrête avant un passage dont le prochain seuil est ≤ 0.4), et une recherche complémentaire de relation utilise 0.8. Ces seuils ne remplacent pas le 0.5 du helper commun.
Ambiguïtés et diagnostic
Le helper est glouton : il cherche une bonne paire au seuil courant, pas la meilleure affectation globale de toutes les rows. Exemple : R1→A=0.9, R1→B=0.8, R2→A=0.85, R2→B=0.1. Il peut retenir R1→A puis laisser R2 sans match, alors que R1→B et R2→A permettraient deux correspondances. Les égalités dépendent de l’ordre. Une valeur NaN ne satisfait aucune comparaison de seuil.
- Identifier le correcteur de page et la liste exacte des candidates.
- Vérifier si un bill_id valide a verrouillé une réponse au passage strict.
- Examiner les champs présents : page, partenaire, date, compte, débit/crédit et montant selon le helper.
- Recalculer le score avec les poids réellement disponibles et vérifier le seuil.
- Contrôler l’ordre des UID, les doublons et les réponses consommées par d’autres entrées.
- Examiner ensuite
errors,expectedValueset les scores bruts; vérifier la pondération danspages_settingsseulement après.