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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/config/routing/admin_accounting/journal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,8 @@ admin_accounting_journal_import:
path: /import
defaults:
_controller: AppBundle\Controller\Admin\Accounting\Journal\ImportAction

admin_accounting_journal_export:
path: /export
defaults:
_controller: AppBundle\Controller\Admin\Accounting\Journal\ExportAction
24 changes: 23 additions & 1 deletion db/seeds/Compta.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public function run(): void
'date_regl' => date('Y-10-16'),
'date_ecriture' => date('Y-10-17'),
'description' => 'Une recette qui rapporte',
'attachment_filename' => $path . '/test_file1.pdf',
'attachment_filename' => $path . 'test_file1.pdf',
'idevenement' => 5,
'idclef' => '',
'idcompte' => 1,
Expand Down Expand Up @@ -130,6 +130,28 @@ public function run(): void
'numero' => '',
'obs_regl' => '',
],
[
'id' => '7',
'idoperation' => 1,
'idcategorie' => 34,
'nom_frs' => 'Le fournisseur',
'tva_intra' => 'FR9912345',
'tva_zone' => 'france',
'montant' => 12003.00,
'idmode_regl' => 2,
'date_regl' => date('Y-11-14'),
'date_ecriture' => date('Y-12-14'),
'description' => 'Description dépense',
'idevenement' => 5,
'idcompte' => 1,
'idclef' => null,
'numero' => '003',
'obs_regl' => 'CB003',
'montant_ht_soumis_tva_20' => 10002.50,
'montant_ht_soumis_tva_10' => 10911.82,
'montant_ht_soumis_tva_5_5' => 11377.25,
'montant_ht_soumis_tva_0' => 12003.00,
],
];

$table = $this->table('compta');
Expand Down
93 changes: 1 addition & 92 deletions htdocs/pages/administration/compta_journal.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
'ajouter',
'modifier',
'modifier_colonne',
'export',
'upload_attachment',
]);

Expand All @@ -39,7 +38,7 @@
$periode_debut = $listPeriode[$id_periode - 1]['date_debut'];
$periode_fin = $listPeriode[$id_periode - 1]['date_fin'];

if (in_array($action, ['lister', 'debit', 'credit', 'export'])) {
if (in_array($action, ['lister', 'debit', 'credit'])) {
$alsoDisplayClassifed = isset($_GET['also_display_classifed_entries']) && $_GET['also_display_classifed_entries'];

$smarty->assign('also_display_classifed_entries', $alsoDisplayClassifed);
Expand Down Expand Up @@ -251,96 +250,6 @@


$smarty->assign('formulaire', genererFormulaire($formulaire));
} elseif ($action === 'export') {
/*
* This action allows the admin to export the full period in a CSV file.
* This is really useful when you need to filter by columns using Excel.
*/
$journal = $compta->obtenirJournal('', $periode_debut, $periode_fin, !$alsoDisplayClassifed);

// Pointer to output
$fp = fopen('php://output', 'w');

// CSV
$csvDelimiter = ';';
$csvEnclosure = '"';
$csvFilename = sprintf(
'AFUP_%s_journal_from-%s_to-%s.csv',
date('Y-M-d'),
$periode_debut,
$periode_fin,
);

// headers
header('Content-Type: text/csv');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"$csvFilename\"");

// First line
$columns = [
'Date',
'Compte',
'Événement',
'Catégorie',
'Description',
'Débit',
'Crédit',
'Règlement',
'Commentaire',
'Justificatif',
'Nom justificatif',
'Montant HT',
'TVA',
'Montant HT non soumis à TVA',
'Montant HT soumis à TVA 5,5',
'TVA 5,5',
'Montant HT soumis à TVA 10',
'TVA 10',
'Montant HT soumis à TVA 20',
'TVA 20',
"Zone de TVA",
];
fputcsv($fp, $columns, $csvDelimiter, $csvEnclosure);

// Set the current local and get variables to use in number_format
$l = setlocale(LC_ALL, 'fr_FR.utf8');
$locale = localeconv();

foreach ($journal as $line) {
$total = number_format((float) $line['montant'], 2, $locale['decimal_point'], $locale['thousands_sep']);
fputcsv(
$fp,
[
$line['date_ecriture'],
$line['nom_compte'],
$line['evenement'],
$line['categorie'],
$line['description'],
$line['idoperation'] == 1 ? "-$total" : '',
$line['idoperation'] != 1 ? $total : '',
$line['reglement'],
$line['comment'],
$line['attachment_required'] ? 'Oui' : 'Non',
$line['attachment_filename'],
$line['montant_ht'],
$line['montant_tva'],
$line['montant_ht_0'],
$line['montant_ht_5_5'],
$line['montant_tva_5_5'],
$line['montant_ht_10'],
$line['montant_tva_10'],
$line['montant_ht_20'],
$line['montant_tva_20'],
Comptabilite::getTvaZoneLabel($line['tva_zone'], 'Non définie'),
],
$csvDelimiter,
$csvEnclosure,
);
}

fclose($fp);

exit;
}

/*
Expand Down
2 changes: 1 addition & 1 deletion htdocs/templates/administration/compta_journal.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ <h2>Journal</h2>
</div>

<div class="ui menu">
<a href="index.php?page=compta_journal&amp;action=export&id_periode={$smarty.get.id_periode|default:''}&also_display_classifed_entries={$also_display_classifed_entries}" class="item">
<a href="/admin/accounting/journal/export?periodId={$smarty.get.id_periode|default:''}&with_reconciled={$also_display_classifed_entries}" class="item">
<i class="icon file"></i>
Exporter la période en CSV
</a>
Expand Down
9 changes: 0 additions & 9 deletions sources/Afup/Comptabilite/Comptabilite.php
Original file line number Diff line number Diff line change
Expand Up @@ -963,13 +963,4 @@ public function modifierRegle($id, $label, $condition, $is_credit, $tva, $catego

return $this->_bdd->executer($requete);
}

public static function getTvaZoneLabel($tvaZoneCode, $defaultValue = null)
{
if (!isset(self::TVA_ZONES[$tvaZoneCode])) {
return $defaultValue;
}

return self::TVA_ZONES[$tvaZoneCode];
}
}
23 changes: 23 additions & 0 deletions sources/AppBundle/Accounting/TvaZone.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace AppBundle\Accounting;

enum TvaZone: string
{
case France = 'france';
case EuropeanUnion = 'ue';
case OutsideEuropeanUnion = 'hors_ue';
case Undefined = '';

public function getLabel(): string
{
return match ($this) {
self::France => 'France',
self::EuropeanUnion => 'Union Européenne hors France',
self::OutsideEuropeanUnion => 'Hors Union Européenne',
self::Undefined => 'Non définie',
};
}
}
106 changes: 106 additions & 0 deletions sources/AppBundle/Controller/Admin/Accounting/Journal/ExportAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\Accounting\Journal;

use AppBundle\Accounting\Model\Repository\TransactionRepository;
use AppBundle\Accounting\Model\Repository\InvoicingPeriodRepository;
use AppBundle\Accounting\TvaZone;
use SplFileObject;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

class ExportAction extends AbstractController
{
public function __construct(
private readonly InvoicingPeriodRepository $invoicingPeriodRepository,
private readonly TransactionRepository $accountingRepository,
) {}

public function __invoke(Request $request): Response
{
$periodId = $request->query->has('periodId') && !empty($request->query->get('periodId')) ? $request->query->getInt('periodId') : null;
$withReconciled = $request->query->getBoolean('with_reconciled');
$period = $this->invoicingPeriodRepository->getCurrentPeriod($periodId);

$entries = $this->accountingRepository->getEntriesPerInvoicingPeriod($period, !$withReconciled);

// CSV
$csvFilename = sprintf(
'AFUP_%s_journal_from-%s_to-%s.csv',
date('Y-M-d'),
$period->getStartDate()->format('Y-m-d'),
$period->getEndDate()->format('Y-m-d'),
);
$tmpFile = tempnam(sys_get_temp_dir(), $csvFilename);
$file = new SplFileObject($tmpFile, 'w');
$csvDelimiter = ';';
$csvEnclosure = '"';

$columns = [
'Date',
'Compte',
'Événement',
'Catégorie',
'Description',
'Débit',
'Crédit',
'Règlement',
'Commentaire',
'Justificatif',
'Nom justificatif',
'Montant HT',
'TVA',
'Montant HT non soumis à TVA',
'Montant HT soumis à TVA 5,5',
'TVA 5,5',
'Montant HT soumis à TVA 10',
'TVA 10',
'Montant HT soumis à TVA 20',
'TVA 20',
"Zone de TVA",
];
$file->fputcsv($columns, $csvDelimiter, $csvEnclosure);

foreach ($entries as $entry) {
$total = number_format((float) $entry['montant'], 2, ',', "\u{202f}");
$file->fputcsv(
[
$entry['date_ecriture'],
$entry['nom_compte'],
$entry['evenement'],
$entry['categorie'],
$entry['description'],
$entry['idoperation'] == 1 ? '-' . $total : '',
$entry['idoperation'] != 1 ? $total : '',
$entry['reglement'],
$entry['comment'],
$entry['attachment_required'] ? 'Oui' : 'Non',
$entry['attachment_filename'],
number_format((float) $entry['montant_ht'], 2, '.', ''),
number_format((float) $entry['montant_tva'], 3, '.', ''),
$entry['montant_ht_0'] ? number_format((float) $entry['montant_ht_0'], 2, '.', '') : $entry['montant_ht_0'],
$entry['montant_ht_5_5'] ? number_format((float) $entry['montant_ht_5_5'], 2, '.', '') : $entry['montant_ht_5_5'],
$entry['montant_tva_5_5'] ? number_format((float) $entry['montant_tva_5_5'], 3, '.', '') : $entry['montant_tva_5_5'],
$entry['montant_ht_10'] ? number_format((float) $entry['montant_ht_10'], 2, '.', '') : $entry['montant_ht_10'],
$entry['montant_tva_10'] ? number_format((float) $entry['montant_tva_10'], 2, '.', '') : $entry['montant_tva_10'],
$entry['montant_ht_20'] ? number_format((float) $entry['montant_ht_20'], 2, '.', '') : $entry['montant_ht_20'],
$entry['montant_tva_20'] ? number_format((float) $entry['montant_tva_20'], 2, '.', '') : $entry['montant_tva_20'],
TvaZone::from((string) $entry['tva_zone'])->getLabel(),
],
$csvDelimiter,
$csvEnclosure,
);
}

$response = new BinaryFileResponse($tmpFile);
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $csvFilename);
$response->deleteFileAfterSend(true);

return $response;
}
}
10 changes: 7 additions & 3 deletions tests/behat/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ public function shouldNotSeeTooltip(string $tooltip): void
}
}

#[Then('/^the downloaded file should be the same as "(?P<value>(?:[^"]|\\")*)"$/')]
public function assertDownloadedFile(string $filename): void
#[Then('/^the downloaded file should (?P<mode>(strictly) )?be the same as "(?P<filename>(?:[^"]|\\")*)"$/')]
public function assertDownloadedFile(string $mode, string $filename): void
{
if ($this->minkContext->getMinkParameter('files_path')) {
$fullPath = rtrim(realpath($this->minkContext->getMinkParameter('files_path')), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $filename;
Expand All @@ -158,7 +158,11 @@ public function assertDownloadedFile(string $filename): void
}
}
$expected = file_get_contents($filename);
$value = $this->minkContext->getSession()->getPage()->getContent();
if ('strictly' === trim($mode)) {
$value = $this->minkContext->getSession()->getDriver()->getContent();
} else {
$value = $this->minkContext->getSession()->getPage()->getContent(); // uses trim()
}

if ($expected !== $value) {
throw new ExpectationException(
Expand Down
8 changes: 7 additions & 1 deletion tests/behat/features/Admin/Tresorerie/Journal.feature
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ Feature: Administration - Trésorerie - Journal

# Export Excel
When I follow "Exporter la période en CSV"
Then the response header "Content-disposition" should match '#^attachment; filename="AFUP_(.*)_journal_from(.*).csv"#'
Then the response header "content-disposition" should match '#^attachment; filename=AFUP_(.*)_journal_from(.*).csv#'
And the downloaded file should strictly be the same as "admin_journal_export.csv"

@reloadDbWithTestData
Scenario: Compte journal Télécharger les justificatifs groupés par mois
Expand Down Expand Up @@ -115,3 +116,8 @@ Feature: Administration - Trésorerie - Journal
Then I should see "Une recette qui rapporte"
And I should see "Une dépense très utile"
And I should see "Une dépense moins utile"

# Export Excel
When I follow "Exporter la période en CSV"
Then the response header "content-disposition" should match '#^attachment; filename=AFUP_(.*)_journal_from(.*).csv#'
And the downloaded file should strictly be the same as "admin_journal_export_reconciled.csv"
3 changes: 3 additions & 0 deletions tests/behat/files/admin_journal_export.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Date;Compte;Événement;Catégorie;Description;Débit;Crédit;Règlement;Commentaire;Justificatif;"Nom justificatif";"Montant HT";TVA;"Montant HT non soumis à TVA";"Montant HT soumis à TVA 5,5";"TVA 5,5";"Montant HT soumis à TVA 10";"TVA 10";"Montant HT soumis à TVA 20";"TVA 20";"Zone de TVA"
2026-01-01;"Compte courant";AG;"A déterminer";"Description dépense 001";-12 001,00;;;;Non;;0.00;0.000;;;;;;;;"Non définie"
2026-01-02;"Compte courant";AG;"A déterminer";"Description recette 001";;12 002,00;;;Non;;0.00;0.000;;;;;;;;"Non définie"
5 changes: 5 additions & 0 deletions tests/behat/files/admin_journal_export_reconciled.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Date;Compte;Événement;Catégorie;Description;Débit;Crédit;Règlement;Commentaire;Justificatif;"Nom justificatif";"Montant HT";TVA;"Montant HT non soumis à TVA";"Montant HT soumis à TVA 5,5";"TVA 5,5";"Montant HT soumis à TVA 10";"TVA 10";"Montant HT soumis à TVA 20";"TVA 20";"Zone de TVA"
2026-10-17;"Compte courant";AG;Assurances;"Une recette qui rapporte";;1 000,00;"Carte Bleue";;Non;202610/test_file1.pdf;0.00;0.000;;;;;;;;"Non définie"
2026-10-18;"Compte courant";AG;Assurances;"Une dépense très utile";-500,00;;"Carte Bleue";;Non;;0.00;0.000;;;;;;;;"Non définie"
2026-11-04;"Compte courant";AG;Assurances;"Une dépense moins utile";-100,00;;"Carte Bleue";;Non;;0.00;0.000;;;;;;;;"Non définie"
2026-12-14;"Compte courant";AG;Assurances;"Description dépense";-12 003,00;;"Carte Bleue";;Non;;44294.57;3717.431;12003.00;11377.25;625.749;10911.82;1091.18;10002.50;2000.50;France
Loading