<?php
namespace ApplicationBundle\Modules\Document\Controller;
use ApplicationBundle\Controller\GenericController;
use ApplicationBundle\Interfaces\SessionCheckInterface;
use ApplicationBundle\Modules\Document\DocumentNumberService;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
/**
* S2.4 — Document Number Sequence admin controller.
*
* Provides:
* - DocumentPrefixListAction GET /document/prefix_list
* - DocumentPrefixResetAction POST /document/prefix_reset (admin only)
* - DocumentSequenceApiAction GET /document/next_number/{type} (internal dev/test)
*/
class DocumentController extends GenericController implements SessionCheckInterface
{
/**
* List all active prefix sequences (current year), grouped by prefix.
* Admin page — information card.
*/
public function DocumentPrefixListAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$year = (int)date('Y');
$tenantId = 1;
$conn = $em->getConnection();
// `document_number_sequences` is created by NOTHING in this codebase: it has no
// Entity and no .orm.yml, so update_database_schema will never provision it, and
// DocumentNumberService writes to it with raw SQL assuming it is already there.
// On a tenant without the table this page 500'd outright. An admin information
// page must not be the thing that breaks — it now says the sequences are not
// provisioned and stays readable, which is also what tells an admin WHY the
// numbering feature is doing nothing.
$rows = [];
$tableMissing = false;
try {
$rows = $conn->fetchAllAssociative(
'SELECT prefix, year, last_seq, tenant_id
FROM document_number_sequences
WHERE tenant_id = ?
ORDER BY prefix ASC, year DESC',
[$tenantId]
);
} catch (\Throwable $e) {
// Only a missing table is tolerated; anything else is a real fault and
// should keep surfacing rather than hiding behind an empty list.
if (stripos($e->getMessage(), 'document_number_sequences') === false) {
throw $e;
}
$tableMissing = true;
}
// Build prefix description map from service
$typeToPrefix = DocumentNumberService::getTypeToPrefix();
$prefixToType = array_flip($typeToPrefix);
return $this->render('@Document/pages/list/list_document_prefixes.html.twig', [
'page_title' => 'Document Number Sequences',
'rows' => $rows,
'prefixToType' => $prefixToType,
'currentYear' => $year,
'tableMissing' => $tableMissing,
]);
}
/**
* AJAX: return the next formatted document number without consuming it.
* Used by forms to preview the number that will be assigned.
* GET /document/peek_number/{type}
*/
public function DocumentPeekNumberAction(Request $request, string $type = 'offer')
{
$em = $this->getDoctrine()->getManager();
$tenantId = 1;
$year = (int)date('Y');
$conn = $em->getConnection();
try {
$prefix = DocumentNumberService::prefixForType($type);
} catch (\RuntimeException $e) {
return new JsonResponse(['error' => $e->getMessage()], 400);
}
$row = $conn->fetchAssociative(
'SELECT last_seq FROM document_number_sequences WHERE tenant_id=? AND prefix=? AND year=?',
[$tenantId, $prefix, $year]
);
$nextSeq = $row ? (int)$row['last_seq'] + 1 : 1;
return new JsonResponse([
'type' => $type,
'prefix' => $prefix,
'year' => $year,
'nextSeq'=> $nextSeq,
'number' => DocumentNumberService::format($prefix, $year, $nextSeq),
]);
}
}