diff --git a/Controller/EditRegularizacionImpuesto.php b/Controller/EditRegularizacionImpuesto.php
index 3cc6de3..4ccc63b 100644
--- a/Controller/EditRegularizacionImpuesto.php
+++ b/Controller/EditRegularizacionImpuesto.php
@@ -93,6 +93,44 @@ protected function checkInvoicesWithoutAccounting($model): void
}
}
+ /**
+ * Builds the common filter used by the summary, purchases/sales and accounting-entry
+ * queries, so the three of them always work over the SAME set of accounting entries.
+ *
+ * @param int $group grupo de cuentas especiales (TAX_ALL, TAX_INPUT, TAX_OUTPUT)
+ * @return array
+ */
+ private function commonTaxWhere(int $group): array
+ {
+ $model = $this->getModel();
+ $excludedOperations = implode(',', [Asiento::OPERATION_OPENING, Asiento::OPERATION_CLOSING]);
+
+ // ids de los asientos de TODAS las regularizaciones (para excluirlos)
+ $regIds = [];
+ foreach (RegularizacionImpuesto::all() as $reg) {
+ if ($reg->idasiento) {
+ $regIds[] = $reg->idasiento;
+ }
+ }
+
+ $subAccountTools = new SubAccountTools();
+ $where = [
+ Where::eq('asientos.idempresa', $model->idempresa),
+ Where::eq('asientos.codejercicio', $model->codejercicio),
+ Where::gte('asientos.fecha', $model->fechainicio),
+ Where::lte('asientos.fecha', $model->fechafin),
+ Where::eq('COALESCE(series.siniva, false)', false),
+ Where::notIn("COALESCE(asientos.operacion, '')", $excludedOperations),
+ $subAccountTools->whereForSpecialAccounts('COALESCE(subcuentas.codcuentaesp, cuentas.codcuentaesp)', $group),
+ ];
+
+ if (false === empty($regIds)) {
+ $where[] = Where::notIn('asientos.idasiento', implode(',', $regIds));
+ }
+
+ return $where;
+ }
+
/**
* Create accounting entry action procedure.
*
@@ -199,6 +237,22 @@ protected function createViewsTaxSummary(string $viewName = 'Modelo303'): void
$this->modelo303 = new Modelo303();
}
+ /**
+ * Setup actions for view.
+ *
+ * @param string $viewName
+ * @param bool $clickable
+ * @return void
+ */
+ private function disableButtons(string $viewName, bool $clickable = false): void
+ {
+ $this->setSettings($viewName, 'btnDelete', false)
+ ->setSettings('btnNew', false)
+ ->setSettings('checkBoxes', false)
+ ->setSettings('clickable', $clickable)
+ ->setSettings('btnPrint', true);
+ }
+
/**
* Run the actions that alter data before reading it.
*
@@ -270,78 +324,6 @@ protected function exportAction(): void
$this->exportManager->show($this->response);
}
- /**
- * Load data view procedure
- *
- * @param string $viewName
- * @param BaseView $view
- * @throws Exception
- */
- protected function loadData($viewName, $view): void
- {
- switch ($viewName) {
- case 'EditRegularizacionImpuesto':
- parent::loadData($viewName, $view);
- $this->settingsMainView();
- break;
-
- case 'ListPartidaImpuestoResumen':
- $mainModel = $this->getModel();
- $excludedOperations = implode(',', [Asiento::OPERATION_OPENING, Asiento::OPERATION_CLOSING]);
- $where = [
- Where::sub([
- Where::like('partidas.codsubcuenta', '477%'),
- Where::orLike('partidas.codsubcuenta', '472%'),
- ]),
- Where::eq('asientos.idempresa', $mainModel->idempresa),
- Where::gte('asientos.fecha', $mainModel->fechainicio),
- Where::lte('asientos.fecha', $mainModel->fechafin),
- Where::eq('COALESCE(series.siniva, false)', false),
- Where::notIn("COALESCE(asientos.operacion, '')", $excludedOperations),
- ];
-
- if (false === empty($mainModel->idasiento)) {
- $where[] = Where::notEq('partidas.idasiento', $mainModel->idasiento);
- }
- $view->loadData(false, $where, [
- 'COALESCE(subcuentas.codcuentaesp, cuentas.codcuentaesp)' => 'ASC',
- 'partidas.codsubcuenta' => 'ASC',
- ]);
-
- $this->modelo303->loadFromResumen($view->cursor); // Load data into Modelo303 View
- $this->checkInvoicesWithoutAccounting($mainModel);
- break;
-
- case 'ListPartida':
- $this->getListPartida($view);
- break;
-
- case 'ListPartidaImpuesto-1':
- $this->getListPartidaImpuesto($view, SubAccountTools::SPECIAL_GROUP_TAX_INPUT);
- break;
-
- case 'ListPartidaImpuesto-2':
- $this->getListPartidaImpuesto($view, SubAccountTools::SPECIAL_GROUP_TAX_OUTPUT);
- break;
- }
- }
-
- /**
- * Setup actions for view.
- *
- * @param string $viewName
- * @param bool $clickable
- * @return void
- */
- private function disableButtons(string $viewName, bool $clickable = false): void
- {
- $this->setSettings($viewName, 'btnDelete', false)
- ->setSettings('btnNew', false)
- ->setSettings('checkBoxes', false)
- ->setSettings('clickable', $clickable)
- ->setSettings('btnPrint', true);
- }
-
/**
* Load data for accounting entry.
*
@@ -383,27 +365,65 @@ private function getListPartidaImpuesto(BaseView $view, int $group): void
*/
private function getPartidaImpuestoWhere(int $group): array
{
- // obtenemos todos los ids de los asientos de las regularizaciones
- $ids = [];
- foreach (RegularizacionImpuesto::all() as $reg) {
- if ($reg->idasiento) {
- $ids[] = $reg->idasiento;
- }
- }
+ return $this->commonTaxWhere($group);
+ }
- $subAccountTools = new SubAccountTools();
- return [
- Where::notIn('asientos.idasiento', implode(',', $ids)),
- Where::eq('asientos.codejercicio', $this->getModel()->codejercicio),
- Where::gte('asientos.fecha', $this->getModel()->fechainicio),
- Where::lte('asientos.fecha', $this->getModel()->fechafin),
- Where::eq('COALESCE(series.siniva, 0)', 0),
- Where::sub([
- Where::notEq('partidas.baseimponible', 0),
- Where::orGt('COALESCE(partidas.iva, 0)', 0),
- ]),
- $subAccountTools->whereForSpecialAccounts('COALESCE(subcuentas.codcuentaesp, cuentas.codcuentaesp)', $group)
- ];
+ /**
+ * Load data view procedure
+ *
+ * @param string $viewName
+ * @param BaseView $view
+ * @throws Exception
+ */
+ protected function loadData($viewName, $view): void
+ {
+ switch ($viewName) {
+ case 'EditRegularizacionImpuesto':
+ parent::loadData($viewName, $view);
+ $this->settingsMainView();
+ break;
+
+ case 'ListPartidaImpuestoResumen':
+ $mainModel = $this->getModel();
+
+ // mismo criterio que las pestañas Compras/Ventas y que el asiento de regularización
+ $where = $this->commonTaxWhere(SubAccountTools::SPECIAL_GROUP_TAX_ALL);
+ $view->loadData(false, $where, [
+ 'COALESCE(subcuentas.codcuentaesp, cuentas.codcuentaesp)' => 'ASC',
+ 'partidas.codsubcuenta' => 'ASC',
+ ]);
+
+ // cargamos el resumen de IVA (casillas del régimen general)
+ $this->modelo303->loadFromResumen($view->cursor);
+
+ // cargamos las bases exentas/informativas de ventas (casillas 59, 60 y 122)
+ $this->modelo303->loadFromSalesInvoices(
+ (int)$mainModel->idempresa,
+ (string)$mainModel->codejercicio,
+ (string)$mainModel->fechainicio,
+ (string)$mainModel->fechafin
+ );
+
+ // mostramos los avisos de importes que no encajan en ninguna casilla
+ foreach ($this->modelo303->getAvisos() as $aviso) {
+ Tools::log()->warning($aviso);
+ }
+
+ $this->checkInvoicesWithoutAccounting($mainModel);
+ break;
+
+ case 'ListPartida':
+ $this->getListPartida($view);
+ break;
+
+ case 'ListPartidaImpuesto-1':
+ $this->getListPartidaImpuesto($view, SubAccountTools::SPECIAL_GROUP_TAX_INPUT);
+ break;
+
+ case 'ListPartidaImpuesto-2':
+ $this->getListPartidaImpuesto($view, SubAccountTools::SPECIAL_GROUP_TAX_OUTPUT);
+ break;
+ }
}
/**
diff --git a/Lib/Accounting/VatRegularizationToAccounting.php b/Lib/Accounting/VatRegularizationToAccounting.php
index 7e19ab7..3fecc43 100644
--- a/Lib/Accounting/VatRegularizationToAccounting.php
+++ b/Lib/Accounting/VatRegularizationToAccounting.php
@@ -123,18 +123,59 @@ protected function addAccountingTaxLines(Asiento $accEntry, RegularizacionImpues
return true;
}
+ /**
+ * Comprueba si hay facturas sin asiento contable
+ *
+ * @param $reg
+ *
+ * @return bool
+ */
+ private function checkInvoicesWithoutAccEntry($reg): bool
+ {
+ $where = [
+ Where::eq('codejercicio', $reg->codejercicio),
+ Where::eq('idempresa', $reg->idempresa),
+ Where::gte('fecha', $reg->fechainicio),
+ Where::lte('fecha', $reg->fechafin),
+ Where::isNull('idasiento'),
+ ];
+
+ $facturasSinAsiento = FacturaCliente::all($where, [], 0, 1);
+ if (false === empty($facturasSinAsiento)) {
+ return false;
+ }
+
+ $facturasSinAsiento = FacturaProveedor::all($where, [], 0, 1);
+ return empty($facturasSinAsiento);
+ }
+
protected function getSubtotals(RegularizacionImpuesto $reg): array
{
$accTools = new SubAccountTools();
$field = 'COALESCE(subcuentas.codcuentaesp, cuentas.codcuentaesp)';
$excludedOperations = implode(',', [Asiento::OPERATION_OPENING, Asiento::OPERATION_CLOSING]);
+
+ // mismo criterio que las pestañas Resumen/Compras/Ventas para que el asiento cuadre con ellas
+ $regIds = [];
+ foreach (RegularizacionImpuesto::all() as $other) {
+ if ($other->idasiento) {
+ $regIds[] = $other->idasiento;
+ }
+ }
+
$where = [
+ Where::eq('asientos.idempresa', $reg->idempresa),
Where::eq('asientos.codejercicio', $reg->codejercicio),
Where::gte('asientos.fecha', $reg->fechainicio),
Where::lte('asientos.fecha', $reg->fechafin),
+ Where::eq('COALESCE(series.siniva, false)', false),
Where::notIn("COALESCE(asientos.operacion, '')", $excludedOperations),
$accTools->whereForSpecialAccounts($field, SubAccountTools::SPECIAL_GROUP_TAX_ALL)
];
+
+ if (false === empty($regIds)) {
+ $where[] = Where::notIn('asientos.idasiento', implode(',', $regIds));
+ }
$orderBy = [
$field => 'ASC',
'partidas.iva' => 'ASC',
@@ -159,30 +200,4 @@ protected function getSubtotals(RegularizacionImpuesto $reg): array
return $subtotals;
}
-
- /**
- * Comprueba si hay facturas sin asiento contable
- *
- * @param $reg
- *
- * @return bool
- */
- private function checkInvoicesWithoutAccEntry($reg): bool
- {
- $where = [
- Where::eq('codejercicio', $reg->codejercicio),
- Where::eq('idempresa', $reg->idempresa),
- Where::gte('fecha', $reg->fechainicio),
- Where::lte('fecha', $reg->fechafin),
- Where::isNull('idasiento'),
- ];
-
- $facturasSinAsiento = FacturaCliente::all($where, [], 0, 1);
- if (false === empty($facturasSinAsiento)) {
- return false;
- }
-
- $facturasSinAsiento = FacturaProveedor::all($where, [], 0, 1);
- return empty($facturasSinAsiento);
- }
}
diff --git a/Lib/Modelo303.php b/Lib/Modelo303.php
index 9983f21..2d169f5 100644
--- a/Lib/Modelo303.php
+++ b/Lib/Modelo303.php
@@ -21,7 +21,10 @@
use FacturaScripts\Core\Base\DataBase;
use FacturaScripts\Core\Tools;
+use FacturaScripts\Core\Where;
+use FacturaScripts\Dinamic\Lib\InvoiceOperation;
use FacturaScripts\Dinamic\Model\Ejercicio;
+use FacturaScripts\Dinamic\Model\FacturaCliente;
use FacturaScripts\Dinamic\Model\Join\PartidaImpuestoResumen;
/**
@@ -34,11 +37,11 @@ class Modelo303
private const MAX_SQUARE = 200;
/**
- * Stores all model squares.
- * Each key is the AEAT square number.
- * '01' => 0.00, '02' => 0.00, ...
+ * Warnings collected while loading data (amounts not assigned to any square).
+ *
+ * @var string[]
*/
- private array $square;
+ private array $avisos = [];
/**
* Structure for know to assign values to squares.
@@ -58,11 +61,10 @@ class Modelo303
'21' => ['base' => '07', 'cuota' => '09'],
],
- // Adquisiciones intracomunitarias
- 'IVARUE' => ['21' => ['base' => '10', 'cuota' => '11']],
-
- // Operaciones con inversión del sujeto pasivo
- // TODO: 'xxxxx' => ['21' => ['base' => '12', 'cuota' => '13']],
+ // Adquisiciones intracomunitarias (cualquier tipo va a 10/11)
+ 'IVARUE' => [
+ '*' => ['base' => '10', 'cuota' => '11'],
+ ],
// Recargo de equivalencia
'IVARRE' => [
@@ -74,38 +76,38 @@ class Modelo303
],
// Operaciones exentas
- 'IVAREX' => ['0' => ['base' => '150', 'cuota' => null]],
+ 'IVAREX' => ['*' => ['base' => '150', 'cuota' => null]],
/*
* IVA soportado (deducible)
* Compras nacionales (régimen general)
*/
'IVASOP' => [
- '21' => ['base' => '28', 'cuota' => '29'],
- '10' => ['base' => '28', 'cuota' => '29'],
- '4' => ['base' => '28', 'cuota' => '29'],
+ '*' => ['base' => '28', 'cuota' => '29'],
],
- // Compras en importaciones
+ // Compras en importaciones (cualquier tipo va a 32/33)
'IVASIM' => [
- '21' => ['base' => '32', 'cuota' => '33'],
- '10' => ['base' => '32', 'cuota' => '33'],
- '4' => ['base' => '32', 'cuota' => '33'],
- '0' => ['base' => '32', 'cuota' => '33'],
+ '*' => ['base' => '32', 'cuota' => '33'],
],
- // Compras en adquisiciones intracomunitarias
+ // Compras en adquisiciones intracomunitarias (cualquier tipo va a 36/37)
'IVASUE' => [
- '21' => ['base' => '36', 'cuota' => '37'],
- '10' => ['base' => '36', 'cuota' => '37'],
- '4' => ['base' => '36', 'cuota' => '37'],
- '0' => ['base' => '36', 'cuota' => '37'],
+ '*' => ['base' => '36', 'cuota' => '37'],
],
- // Operaciones exentas
- 'IVASEX' => ['0' => ['base' => '60', 'cuota' => null]],
+ // Operaciones exentas: las compras exentas no se deducen ni figuran en el
+ // resultado del régimen general; se reconocen para no generar avisos.
+ 'IVASEX' => ['*' => ['base' => null, 'cuota' => null]],
];
+ /**
+ * Stores all model squares.
+ * Each key is the AEAT square number.
+ * '01' => 0.00, '02' => 0.00, ...
+ */
+ private array $square;
+
/**
* Initializes the tax rates for each square.
*/
@@ -154,6 +156,17 @@ public function casillaStr(string $square, bool $showEmpty = false): string
return Tools::number($value, 2);
}
+ /**
+ * Returns the list of warnings collected while loading data
+ * (amounts that could not be assigned to any square).
+ *
+ * @return string[]
+ */
+ public function getAvisos(): array
+ {
+ return $this->avisos;
+ }
+
/**
* Loads summary data from an array of PartidaImpuestoResumen.
*
@@ -164,6 +177,8 @@ public function loadFromResumen(array $resumen): void
foreach ($resumen as $item) {
$this->addMovimiento(
$item->codcuentaesp ?? '',
+ $item->operacion ?? '',
+ $item->tipodoc ?? '',
(float) $item->iva,
(float) $item->recargo,
(float) $item->baseimponible,
@@ -173,6 +188,35 @@ public function loadFromResumen(array $resumen): void
$this->calculateTotals();
}
+ /**
+ * Loads the exempt/informative tax bases of sales invoices (without VAT entries)
+ * into their squares: intracommunity (59), exports (60) and reverse charge (122).
+ *
+ * @param int $idempresa
+ * @param string $codejercicio
+ * @param string $dateStart
+ * @param string $dateEnd
+ */
+ public function loadFromSalesInvoices(int $idempresa, string $codejercicio, string $dateStart, string $dateEnd): void
+ {
+ $where = [
+ Where::eq('idempresa', $idempresa),
+ Where::eq('codejercicio', $codejercicio),
+ Where::gte('fecha', $dateStart),
+ Where::lte('fecha', $dateEnd),
+ Where::in('operacion', [
+ InvoiceOperation::INTRA_COMMUNITY,
+ InvoiceOperation::INTRA_COMMUNITY_SERVICES,
+ InvoiceOperation::EXPORT,
+ InvoiceOperation::REVERSE_CHARGE,
+ ]),
+ ];
+
+ foreach (FacturaCliente::all($where, [], 0, 0) as $invoice) {
+ $this->addBaseExentaVenta((string)$invoice->operacion, (float)$invoice->neto);
+ }
+ }
+
public static function treasury(string $codejercicio, string $period): array
{
// comprobamos que el ejercicio existe
@@ -192,21 +236,76 @@ public static function treasury(string $codejercicio, string $period): array
];
}
+ /**
+ * Adds the exempt tax base of a sales invoice to its informative square.
+ *
+ * @param string $operacion
+ * @param float $base
+ * @return void
+ */
+ private function addBaseExentaVenta(string $operacion, float $base): void
+ {
+ switch ($operacion) {
+ case InvoiceOperation::EXPORT:
+ $this->square['60'] += $base;
+ break;
+
+ case InvoiceOperation::INTRA_COMMUNITY:
+ case InvoiceOperation::INTRA_COMMUNITY_SERVICES:
+ $this->square['59'] += $base;
+ break;
+
+ case InvoiceOperation::REVERSE_CHARGE:
+ $this->square['122'] += $base;
+ break;
+ }
+ }
+
+ /**
+ * Adds a base and/or quota amount to the given squares.
+ *
+ * @param string|null $baseSquare
+ * @param string|null $cuotaSquare
+ * @param float $base
+ * @param float $cuota
+ * @return void
+ */
+ private function addCasilla(?string $baseSquare, ?string $cuotaSquare, float $base, float $cuota): void
+ {
+ if (false === empty($baseSquare)) {
+ $this->square[$baseSquare] += $base;
+ }
+
+ if (false === empty($cuotaSquare)) {
+ $this->square[$cuotaSquare] += $cuota;
+ }
+ }
+
/**
* Add a tax movement to the model (base + quota by type and rate)
- * - Determine the correct square based on the type and tax rate.
- * - Update the base and quota squares accordingly.
+ * - Special operations (reverse charge, intracommunity, import) are routed by
+ * the invoice operation type to their specific squares.
+ * - The rest follows the casillaMap by special account and tax rate.
*
- * @param string $tipo
+ * @param string $tipo cuenta especial de IVA (IVAREP, IVASOP, IVARUE, ...)
+ * @param string $operacion tipo de operación de la factura (intracomunitaria, inv-sujeto-pasivo, ...)
+ * @param string $tipodoc 'compra' o 'venta'
* @param float $iva
* @param float $recargo
* @param float $base
* @param float $cuota
* @return void
*/
- private function addMovimiento(string $tipo, float $iva, float $recargo, float $base, float $cuota): void
+ private function addMovimiento(string $tipo, string $operacion, string $tipodoc, float $iva, float $recargo, float $base, float $cuota): void
{
+ // Las operaciones especiales (ISP, intracomunitarias, importación) deciden la casilla
+ // por el tipo de operación de la factura, no solo por la cuenta especial.
+ if ($this->addMovimientoEspecial($tipo, $operacion, $tipodoc, $base, $cuota)) {
+ return;
+ }
+
if (false === isset($this->casillaMap[$tipo])) {
+ $this->registrarNoMapeado($tipo, $operacion, $base, $cuota);
return;
}
@@ -219,20 +318,77 @@ private function addMovimiento(string $tipo, float $iva, float $recargo, float $
?? null;
if ($grupo === null) {
+ $this->registrarNoMapeado($tipo, $operacion, $base, $cuota, $tax);
return;
}
- // Update base and quota squares.
- if (false === empty($grupo['base'])) {
- $this->square[$grupo['base']] += $base;
+ // For recargo, if cuota is zero, calculate it from base and recargo rate
+ if ($tipo === 'IVARRE' && $cuota == 0.0 && $recargo > 0.0) {
+ $cuota = $base * ($recargo / 100.0);
}
- if (false === empty($grupo['cuota'])) {
- // For recargo, if cuota is zero, calculate it from base and recargo rate
- if ($tipo === 'IVARRE' && $cuota == 0.0 && $recargo > 0.0) {
- $cuota = $base * ($recargo / 100.0);
- }
- $this->square[$grupo['cuota']] += $cuota;
+ $this->addCasilla($grupo['base'] ?? null, $grupo['cuota'] ?? null, $base, $cuota);
+ }
+
+ /**
+ * Routes movements of special invoice operations (reverse charge, intracommunity, import)
+ * to their specific squares. Returns true if the movement has been handled (or intentionally
+ * skipped) and must not follow the standard mapping.
+ *
+ * @param string $tipo
+ * @param string $operacion
+ * @param string $tipodoc
+ * @param float $base
+ * @param float $cuota
+ * @return bool
+ */
+ private function addMovimientoEspecial(string $tipo, string $operacion, string $tipodoc, float $base, float $cuota): bool
+ {
+ $esDevengado = in_array($tipo, ['IVAREP', 'IVARUE', 'IVAREX'], true);
+ $esDeducible = in_array($tipo, ['IVASOP', 'IVASUE', 'IVASIM', 'IVASEX'], true);
+
+ // El recargo de equivalencia y cuentas no fiscales siguen el tratamiento estándar.
+ if (false === $esDevengado && false === $esDeducible) {
+ return false;
+ }
+
+ switch ($operacion) {
+ case InvoiceOperation::REVERSE_CHARGE:
+ // Inversión del sujeto pasivo en COMPRAS: autorrepercusión.
+ // Devengado → 12/13, deducible → 28/29 (el IVA se compensa).
+ if ($tipodoc === 'compra') {
+ $esDevengado
+ ? $this->addCasilla('12', '13', $base, $cuota)
+ : $this->addCasilla('28', '29', $base, $cuota);
+ }
+ // En ventas con ISP el vendedor no repercute IVA; la base informativa (122)
+ // se carga desde las facturas en loadFromSalesInvoices().
+ return true;
+
+ case InvoiceOperation::INTRA_COMMUNITY:
+ case InvoiceOperation::INTRA_COMMUNITY_SERVICES:
+ // Adquisición intracomunitaria (COMPRA): devengado → 10/11, deducible → 36/37.
+ if ($tipodoc === 'compra') {
+ $esDevengado
+ ? $this->addCasilla('10', '11', $base, $cuota)
+ : $this->addCasilla('36', '37', $base, $cuota);
+ }
+ // Entrega intracomunitaria (VENTA): exenta. Las partidas de autorrepercusión
+ // (IVARUE/IVASUE) se compensan y no forman parte del régimen general; la base
+ // informativa (casilla 59) se carga desde las facturas.
+ return true;
+
+ case InvoiceOperation::IMPORT:
+ // Importación (COMPRA): el IVA lo liquida la aduana (DUA). Solo el soportado deducible.
+ if ($esDeducible) {
+ $this->addCasilla('32', '33', $base, $cuota);
+ }
+ // El IVA devengado de importación (casilla 77 / IVA diferido) no tiene origen
+ // contable automático en FacturaScripts.
+ return true;
+
+ default:
+ return false;
}
}
@@ -271,23 +427,31 @@ private function calculateTotals(): void
$this->square['46'] = $this->square['27'] - $this->square['45'];
}
- protected static function treasurySaldoCuenta(string $cuenta, string $desde, string $hasta, DataBase $dataBase): float
+ /**
+ * Records a warning for an amount that could not be assigned to any square,
+ * so the user can understand why the summary may not match the purchases/sales tabs.
+ *
+ * @param string $tipo
+ * @param string $operacion
+ * @param float $base
+ * @param float $cuota
+ * @param float|null $tax
+ * @return void
+ */
+ private function registrarNoMapeado(string $tipo, string $operacion, float $base, float $cuota, ?float $tax = null): void
{
- $saldo = 0.0;
-
- if ($dataBase->tableExists('partidas')) {
- // calculamos el saldo de todos aquellos asientos que afecten a caja
- $sql = "select sum(debe-haber) as total from partidas where codsubcuenta LIKE " . $dataBase->var2str($cuenta)
- . " and idasiento in (select idasiento from asientos where fecha >= " . $dataBase->var2str($desde)
- . " and fecha <= " . $dataBase->var2str($hasta) . ");";
-
- $data = $dataBase->select($sql);
- if ($data && $data[0]['total'] !== null) {
- $saldo = floatval($data[0]['total']);
- }
+ // No avisamos si no hay importe relevante.
+ if (empty($base) && empty($cuota)) {
+ return;
}
- return $saldo;
+ $this->avisos[] = Tools::lang()->trans('model303-amount-without-square', [
+ '%type%' => $tipo,
+ '%rate%' => $tax === null ? '-' : Tools::number($tax, 2),
+ '%operation%' => $operacion === '' ? '-' : $operacion,
+ '%base%' => Tools::number($base, 2),
+ '%quota%' => Tools::number($cuota, 2),
+ ]);
}
protected static function treasuryDates(Ejercicio $exercise, string $period): array
@@ -320,4 +484,23 @@ protected static function treasuryDates(Ejercicio $exercise, string $period): ar
],
};
}
+
+ protected static function treasurySaldoCuenta(string $cuenta, string $desde, string $hasta, DataBase $dataBase): float
+ {
+ $saldo = 0.0;
+
+ if ($dataBase->tableExists('partidas')) {
+ // calculamos el saldo de todos aquellos asientos que afecten a caja
+ $sql = "select sum(debe-haber) as total from partidas where codsubcuenta LIKE " . $dataBase->var2str($cuenta)
+ . " and idasiento in (select idasiento from asientos where fecha >= " . $dataBase->var2str($desde)
+ . " and fecha <= " . $dataBase->var2str($hasta) . ");";
+
+ $data = $dataBase->select($sql);
+ if ($data && $data[0]['total'] !== null) {
+ $saldo = floatval($data[0]['total']);
+ }
+ }
+
+ return $saldo;
+ }
}
diff --git a/Model/Join/PartidaImpuesto.php b/Model/Join/PartidaImpuesto.php
index dd0cb64..b37e700 100644
--- a/Model/Join/PartidaImpuesto.php
+++ b/Model/Join/PartidaImpuesto.php
@@ -127,19 +127,23 @@ protected function loadFromData(array $data): void
{
parent::loadFromData($data);
- if ($this->iva > 0 && $this->recargo > 0) {
- $this->cuotaiva = $this->baseimponible * ($this->iva / 100.0);
- $this->cuotarecargo = $this->baseimponible * ($this->recargo / 100.0);
- } elseif ($this->iva > 0) {
- $this->cuotaiva = $this->codcuentaesp === 'IVAREP'
- ? $data['haber'] - $data['debe']
- : $data['debe'] - $data['haber'];
- $this->cuotarecargo = 0.0;
- } else {
- $this->cuotarecargo = $this->codcuentaesp === 'IVAREP'
- ? $data['haber'] - $data['debe']
- : $data['debe'] - $data['haber'];
+ // La cuota es SIEMPRE el importe realmente contabilizado (debe/haber), nunca un
+ // recálculo base*tipo (que introduce desfases de céntimos). Usamos la misma fórmula
+ // que PartidaImpuestoResumen: debe+haber da el importe en positivo (una de las dos
+ // columnas siempre es 0) y solo invertimos el signo en bases negativas (rectificativas).
+ $debe = (float)($data['debe'] ?? 0.0);
+ $haber = (float)($data['haber'] ?? 0.0);
+ $cuota = ($this->baseimponible < 0 && ($debe + $haber) > 0)
+ ? ($debe + $haber) * -1
+ : $debe + $haber;
+
+ // El destino (IVA o recargo) lo determina la cuenta especial, igual que en el resumen.
+ if ($this->codcuentaesp === 'IVARRE') {
+ $this->cuotarecargo = $cuota;
$this->cuotaiva = 0.0;
+ } else {
+ $this->cuotaiva = $cuota;
+ $this->cuotarecargo = 0.0;
}
// si el campo factura está vacío, buscamos la factura con este asiento
diff --git a/Model/Join/PartidaImpuestoResumen.php b/Model/Join/PartidaImpuestoResumen.php
index d63c850..b17fdf8 100644
--- a/Model/Join/PartidaImpuestoResumen.php
+++ b/Model/Join/PartidaImpuestoResumen.php
@@ -45,6 +45,15 @@ protected function getFields(): array
'codcuentaesp' => 'COALESCE(subcuentas.codcuentaesp, cuentas.codcuentaesp)',
'tipo_desc' => 'cuentasesp.descripcion',
+ // operación de la factura (compra o venta) que originó el asiento,
+ // necesaria para decidir las casillas del 303 en operaciones especiales
+ // (intracomunitaria, inversión del sujeto pasivo, importación, etc.)
+ 'operacion' => "COALESCE(facturasprov.operacion, facturascli.operacion, '')",
+
+ // tipo de documento: una compra y una venta intracomunitarias generan las mismas
+ // partidas de IVA (IVARUE/IVASUE), pero van a casillas distintas del 303.
+ 'tipodoc' => $this->sqlForTipoDoc(),
+
'baseimponible' => $this->sqlForRoundedSum('partidas.baseimponible'),
'debe' => $this->sqlForRoundedSum('partidas.debe'),
'haber' => $this->sqlForRoundedSum('partidas.haber'),
@@ -65,7 +74,9 @@ protected function getGroupFields(): string
. ', partidas.recargo'
. ', subcuentas.descripcion'
. ', COALESCE(subcuentas.codcuentaesp, cuentas.codcuentaesp)'
- . ', cuentasesp.descripcion';
+ . ', cuentasesp.descripcion'
+ . ", COALESCE(facturasprov.operacion, facturascli.operacion, '')"
+ . ', ' . $this->sqlForTipoDoc();
}
/**
@@ -80,7 +91,12 @@ protected function getSQLFrom(): string
. ' INNER JOIN subcuentas on subcuentas.idsubcuenta = partidas.idsubcuenta'
. ' INNER JOIN cuentas on cuentas.idcuenta = subcuentas.idcuenta'
. ' LEFT JOIN cuentasesp on cuentasesp.codcuentaesp = coalesce(subcuentas.codcuentaesp, cuentas.codcuentaesp)'
- . ' LEFT JOIN series on series.codserie = partidas.codserie';
+ . ' LEFT JOIN series on series.codserie = partidas.codserie'
+ // enlazamos la factura por el asiento contable para conocer el tipo de operación.
+ // Usamos subconsultas que solo exponen idasiento y operacion para no introducir
+ // columnas duplicadas (p.ej. "fecha") que harían ambiguos otros filtros.
+ . ' LEFT JOIN (SELECT idasiento, operacion FROM facturasprov) facturasprov on facturasprov.idasiento = asientos.idasiento'
+ . ' LEFT JOIN (SELECT idasiento, operacion FROM facturascli) facturascli on facturascli.idasiento = asientos.idasiento';
}
/**
@@ -97,6 +113,8 @@ protected function getTables(): array
'cuentas',
'cuentasesp',
'series',
+ 'facturasprov',
+ 'facturascli',
];
}
@@ -137,4 +155,16 @@ private function sqlForRoundedSum(string $field): string
{
return 'ROUND(CAST(SUM(' . $field . ') AS DECIMAL(20, 6)), 2)';
}
+
+ /**
+ * SQL snippet to know if the entry comes from a purchase or a sale invoice.
+ *
+ * @return string
+ */
+ private function sqlForTipoDoc(): string
+ {
+ return "CASE WHEN facturasprov.idasiento IS NOT NULL THEN 'compra'
+ WHEN facturascli.idasiento IS NOT NULL THEN 'venta'
+ ELSE '' END";
+ }
}
diff --git a/Test/main/Modelo303SquaresTest.php b/Test/main/Modelo303SquaresTest.php
new file mode 100644
index 0000000..f55705c
--- /dev/null
+++ b/Test/main/Modelo303SquaresTest.php
@@ -0,0 +1,202 @@
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see .
+ */
+
+namespace FacturaScripts\Test\Plugins;
+
+use FacturaScripts\Dinamic\Lib\InvoiceOperation;
+use FacturaScripts\Plugins\Modelo303\Lib\Modelo303;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Pruebas unitarias del reparto de importes a casillas del modelo 303.
+ * No necesita base de datos: alimenta Modelo303 con filas simuladas del resumen.
+ */
+final class Modelo303SquaresTest extends TestCase
+{
+ public function testCompraImportacion(): void
+ {
+ $modelo = new Modelo303();
+ $modelo->loadFromResumen([
+ $this->row('IVASIM', 21, 5000.0, 1050.0, InvoiceOperation::IMPORT, 'compra'),
+ ]);
+
+ $this->assertEqualsWithDelta(5000.0, $modelo->casilla('32'), 0.001);
+ $this->assertEqualsWithDelta(1050.0, $modelo->casilla('33'), 0.001);
+ }
+
+ public function testCompraIntracomunitaria(): void
+ {
+ $modelo = new Modelo303();
+ // intracomunitaria con cuentas específicas IVARUE/IVASUE
+ $modelo->loadFromResumen([
+ $this->row('IVARUE', 21, 4000.0, 840.0, InvoiceOperation::INTRA_COMMUNITY, 'compra'),
+ $this->row('IVASUE', 21, 4000.0, 840.0, InvoiceOperation::INTRA_COMMUNITY, 'compra'),
+ ]);
+
+ // devengado -> 10/11
+ $this->assertEqualsWithDelta(4000.0, $modelo->casilla('10'), 0.001);
+ $this->assertEqualsWithDelta(840.0, $modelo->casilla('11'), 0.001);
+ // deducible -> 36/37
+ $this->assertEqualsWithDelta(4000.0, $modelo->casilla('36'), 0.001);
+ $this->assertEqualsWithDelta(840.0, $modelo->casilla('37'), 0.001);
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('46'), 0.001);
+ }
+
+ public function testCompraIntracomunitariaConCuentasGenericas(): void
+ {
+ $modelo = new Modelo303();
+ // intracomunitaria pero contabilizada en IVAREP/IVASOP genéricos:
+ // el tipo de operación debe forzar igualmente 10/11 y 36/37
+ $modelo->loadFromResumen([
+ $this->row('IVAREP', 21, 1000.0, 210.0, InvoiceOperation::INTRA_COMMUNITY_SERVICES, 'compra'),
+ $this->row('IVASOP', 21, 1000.0, 210.0, InvoiceOperation::INTRA_COMMUNITY_SERVICES, 'compra'),
+ ]);
+
+ $this->assertEqualsWithDelta(1000.0, $modelo->casilla('10'), 0.001);
+ $this->assertEqualsWithDelta(210.0, $modelo->casilla('11'), 0.001);
+ $this->assertEqualsWithDelta(1000.0, $modelo->casilla('36'), 0.001);
+ $this->assertEqualsWithDelta(210.0, $modelo->casilla('37'), 0.001);
+ // no debe ir a las casillas nacionales (07/09) ni a las de ISP (12/13)
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('09'), 0.001);
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('13'), 0.001);
+ }
+
+ public function testCompraInversionSujetoPasivo(): void
+ {
+ $modelo = new Modelo303();
+ // autorrepercusión: IVAREP (devengado) + IVASOP (deducible), mismo importe
+ $modelo->loadFromResumen([
+ $this->row('IVAREP', 21, 3000.0, 630.0, InvoiceOperation::REVERSE_CHARGE, 'compra'),
+ $this->row('IVASOP', 21, 3000.0, 630.0, InvoiceOperation::REVERSE_CHARGE, 'compra'),
+ ]);
+
+ // devengado -> 12/13
+ $this->assertEqualsWithDelta(3000.0, $modelo->casilla('12'), 0.001);
+ $this->assertEqualsWithDelta(630.0, $modelo->casilla('13'), 0.001);
+ // deducible -> 28/29
+ $this->assertEqualsWithDelta(3000.0, $modelo->casilla('28'), 0.001);
+ $this->assertEqualsWithDelta(630.0, $modelo->casilla('29'), 0.001);
+
+ // el IVA se compensa: resultado del régimen general (46) = 0
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('46'), 0.001);
+ // y NO debe contaminar las casillas de ventas nacionales (07/09)
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('09'), 0.001);
+ }
+
+ public function testCompraNacionalDeducible(): void
+ {
+ $modelo = new Modelo303();
+ $modelo->loadFromResumen([
+ $this->row('IVASOP', 21, 2000.0, 420.0, '', 'compra'),
+ // tipo no contemplado en el mapa antiguo (5%): debe sumarse igual via comodín
+ $this->row('IVASOP', 5, 100.0, 5.0, '', 'compra'),
+ ]);
+
+ $this->assertEqualsWithDelta(2100.0, $modelo->casilla('28'), 0.001);
+ $this->assertEqualsWithDelta(425.0, $modelo->casilla('29'), 0.001);
+ $this->assertEqualsWithDelta(425.0, $modelo->casilla('45'), 0.001);
+ $this->assertEmpty($modelo->getAvisos());
+ }
+
+ public function testImporteSinCasillaGeneraAviso(): void
+ {
+ $modelo = new Modelo303();
+ // cuenta especial desconocida con importe: debe generar aviso y no perderse en silencio
+ $modelo->loadFromResumen([
+ $this->row('IVAXXX', 21, 1000.0, 210.0, '', 'venta'),
+ ]);
+
+ $this->assertNotEmpty($modelo->getAvisos());
+ }
+
+ public function testRecargoEquivalencia(): void
+ {
+ $modelo = new Modelo303();
+ $modelo->loadFromResumen([
+ $this->row('IVARRE', 21, 1000.0, 52.0, '', 'venta', 5.2),
+ ]);
+
+ // recargo 5.2 -> base 22 / cuota 24
+ $this->assertEqualsWithDelta(1000.0, $modelo->casilla('22'), 0.001);
+ $this->assertEqualsWithDelta(52.0, $modelo->casilla('24'), 0.001);
+ }
+
+ public function testVentaIntracomunitariaNoContaminaRegimenGeneral(): void
+ {
+ $modelo = new Modelo303();
+ // una venta intracomunitaria genera IVARUE/IVASUE (neto cero); NO debe sumar en 10/11/36/37
+ $modelo->loadFromResumen([
+ $this->row('IVARUE', 21, 7000.0, 1470.0, InvoiceOperation::INTRA_COMMUNITY, 'venta'),
+ $this->row('IVASUE', 21, 7000.0, 1470.0, InvoiceOperation::INTRA_COMMUNITY, 'venta'),
+ ]);
+
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('10'), 0.001);
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('11'), 0.001);
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('36'), 0.001);
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('37'), 0.001);
+ $this->assertEqualsWithDelta(0.0, $modelo->casilla('46'), 0.001);
+ }
+
+ public function testVentaNacionalRegimenGeneral(): void
+ {
+ $modelo = new Modelo303();
+ $modelo->loadFromResumen([
+ $this->row('IVAREP', 21, 1000.0, 210.0),
+ $this->row('IVAREP', 10, 500.0, 50.0),
+ $this->row('IVAREP', 4, 100.0, 4.0),
+ ]);
+
+ // 21% -> base 07 / cuota 09
+ $this->assertEqualsWithDelta(1000.0, $modelo->casilla('07'), 0.001);
+ $this->assertEqualsWithDelta(210.0, $modelo->casilla('09'), 0.001);
+ // 10% -> base 04 / cuota 06
+ $this->assertEqualsWithDelta(500.0, $modelo->casilla('04'), 0.001);
+ $this->assertEqualsWithDelta(50.0, $modelo->casilla('06'), 0.001);
+ // 4% -> base 01 / cuota 03
+ $this->assertEqualsWithDelta(100.0, $modelo->casilla('01'), 0.001);
+ $this->assertEqualsWithDelta(4.0, $modelo->casilla('03'), 0.001);
+
+ // total devengado (casilla 27) = 210 + 50 + 4
+ $this->assertEqualsWithDelta(264.0, $modelo->casilla('27'), 0.001);
+ $this->assertEmpty($modelo->getAvisos());
+ }
+
+ /**
+ * Crea una fila equivalente a una de PartidaImpuestoResumen.
+ */
+ private function row(
+ string $codcuentaesp,
+ float $iva,
+ float $base,
+ float $cuota,
+ string $operacion = '',
+ string $tipodoc = '',
+ float $recargo = 0.0
+ ): object {
+ return (object)[
+ 'codcuentaesp' => $codcuentaesp,
+ 'operacion' => $operacion,
+ 'tipodoc' => $tipodoc,
+ 'iva' => $iva,
+ 'recargo' => $recargo,
+ 'baseimponible' => $base,
+ 'cuota' => $cuota,
+ ];
+ }
+}
diff --git a/Translation/es_ES.json b/Translation/es_ES.json
index 61aec41..d6e83a6 100644
--- a/Translation/es_ES.json
+++ b/Translation/es_ES.json
@@ -21,5 +21,10 @@
"year-model-390": "Año (modelo 390)",
"deductible-vat-28": "por operaciones interiores corrientes",
"deductible-vat-32": "por importaciones",
- "deductible-vat-36": "por adquisiciones intracomunitarias"
+ "deductible-vat-36": "por adquisiciones intracomunitarias",
+ "info-operations-no-vat": "Operaciones sin repercusión de IVA (informativas)",
+ "intra-community-deliveries": "Entregas intracomunitarias de bienes y servicios",
+ "exports": "Exportaciones y operaciones asimiladas",
+ "sales-reverse-charge": "Ventas con inversión del sujeto pasivo",
+ "model303-amount-without-square": "Hay un importe que no se refleja en ninguna casilla del modelo 303: cuenta %type%, tipo %rate%%, operación %operation%, base %base%, cuota %quota%. Revise la configuración de cuentas especiales o el tipo de operación de la factura."
}
diff --git a/View/Modelo303.html.twig b/View/Modelo303.html.twig
index dccd27c..631fb83 100644
--- a/View/Modelo303.html.twig
+++ b/View/Modelo303.html.twig
@@ -1,291 +1,354 @@
-
-
-
-
-
-
- |
- {{ trans('tax-base') }} |
- |
- {{ trans('type') }} % |
- |
- {{ trans('quota') }} |
-
-
-
-
- | 150 |
-
- {{ fsc.modelo303.casillaStr('150') }} |
- 151 |
-
- {{ fsc.modelo303.casillaStr('151', true) }} |
- 152 |
-
- {{ fsc.modelo303.casillaStr('152') }} |
-
-
- | 165 |
-
- {{ fsc.modelo303.casillaStr('165') }} |
- 166 |
-
- {{ fsc.modelo303.casillaStr('166') }} |
- 167 |
-
- {{ fsc.modelo303.casillaStr('167') }} |
-
-
- | 01 |
-
- {{ fsc.modelo303.casillaStr('01') }}
- |
- 02 |
-
- {{ fsc.modelo303.casillaStr('02') }} |
- 03 |
-
- {{ fsc.modelo303.casillaStr('03') }}
- |
-
-
- | 153 |
-
- {{ fsc.modelo303.casillaStr('153') }} |
- 154 |
-
- {{ fsc.modelo303.casillaStr('154') }} |
- 155 |
-
- {{ fsc.modelo303.casillaStr('155') }} |
-
-
- | 04 |
-
- {{ fsc.modelo303.casillaStr('04') }}
- |
- 05 |
-
- {{ fsc.modelo303.casillaStr('05') }} |
- 06 |
-
- {{ fsc.modelo303.casillaStr('06') }}
- |
-
-
- | 07 |
-
- {{ fsc.modelo303.casillaStr('07') }}
- |
- 08 |
-
- {{ fsc.modelo303.casillaStr('08') }} |
- 09 |
-
- {{ fsc.modelo303.casillaStr('09') }}
- |
-
-
- | 10 |
-
- {{ fsc.modelo303.casillaStr('10') }}
- |
- |
- 11 |
-
- {{ fsc.modelo303.casillaStr('11') }}
- |
-
-
- | 12 |
-
- {{ fsc.modelo303.casillaStr('12') }}
- |
- |
- 13 |
-
- {{ fsc.modelo303.casillaStr('13') }}
- |
-
-
- | 14 |
-
- {{ fsc.modelo303.casillaStr('14') }}
- |
- |
- 15 |
-
- {{ fsc.modelo303.casillaStr('15') }}
- |
-
-
- | 156 |
-
- {{ fsc.modelo303.casillaStr('156') }} |
- 157 |
-
- {{ fsc.modelo303.casillaStr('157') }} |
- 158 |
-
- {{ fsc.modelo303.casillaStr('158') }} |
-
-
- | 168 |
-
- {{ fsc.modelo303.casillaStr('168') }} |
- 169 |
-
- {{ fsc.modelo303.casillaStr('169') }} |
- 170 |
-
- {{ fsc.modelo303.casillaStr('170') }} |
-
-
- | 16 |
-
- {{ fsc.modelo303.casillaStr('16') }}
- |
- 17 |
-
- {{ fsc.modelo303.casillaStr('17') }} |
- 18 |
-
- {{ fsc.modelo303.casillaStr('18') }}
- |
-
-
- | 19 |
-
- {{ fsc.modelo303.casillaStr('19') }}
- |
- 20 |
-
- {{ fsc.modelo303.casillaStr('20') }} |
- 21 |
-
- {{ fsc.modelo303.casillaStr('21') }}
- |
-
-
- | 22 |
-
- {{ fsc.modelo303.casillaStr('22') }}
- |
- 23 |
-
- {{ fsc.modelo303.casillaStr('23') }} |
- 24 |
-
- {{ fsc.modelo303.casillaStr('24') }}
- |
-
-
- | 25 |
-
- {{ fsc.modelo303.casillaStr('25') }}
- |
- |
- 26 |
-
- {{ fsc.modelo303.casillaStr('26') }}
- |
-
-
-
-
-
-
- | {{ trans('total-accrued-fee') }} |
- 27 |
-
- {{ fsc.modelo303.casillaStr('27') }}
- |
-
-
-
-
-
-
-
-
-
-
-
- |
- |
- {{ trans('base') }} |
- |
- {{ trans('quota') }} |
-
-
-
-
- | {{ trans('deductible-vat-28') }} |
- 28 |
-
- {{ fsc.modelo303.casillaStr('28') }}
- |
- 29 |
-
- {{ fsc.modelo303.casillaStr('29') }}
- |
-
-
- | {{ trans('deductible-vat-32') }} |
- 32 |
-
- {{ fsc.modelo303.casillaStr('32') }}
- |
- 33 |
-
- {{ fsc.modelo303.casillaStr('33') }}
- |
-
-
- | {{ trans('deductible-vat-36') }} |
- 36 |
-
- {{ fsc.modelo303.casillaStr('36') }}
- |
- 37 |
-
- {{ fsc.modelo303.casillaStr('37') }}
- |
-
-
-
-
-
-
- | {{ trans('total-to-deduct') }} |
- 45 |
-
- {{ fsc.modelo303.casillaStr('45') }}
- |
-
-
-
-
-
-
-
-
-
- | {{ trans('total-result-of-the-general-regime') }} |
- 46 |
-
- {{ fsc.modelo303.casillaStr('46') }}
- |
-
-
-
-
-
-
+
+
+
+
+
+
+ |
+ {{ trans('tax-base') }} |
+ |
+ {{ trans('type') }}
+ % |
+ |
+ {{ trans('quota') }} |
+
+
+
+
+ | 150 |
+
+ {{ fsc.modelo303.casillaStr('150') }}
+ |
+ 151 |
+
+ {{ fsc.modelo303.casillaStr('151', true) }}
+ |
+ 152 |
+
+ {{ fsc.modelo303.casillaStr('152') }}
+ |
+
+
+ | 165 |
+
+ {{ fsc.modelo303.casillaStr('165') }}
+ |
+ 166 |
+
+ {{ fsc.modelo303.casillaStr('166') }}
+ |
+ 167 |
+
+ {{ fsc.modelo303.casillaStr('167') }}
+ |
+
+
+ | 01 |
+
+ {{ fsc.modelo303.casillaStr('01') }}
+ |
+ 02 |
+
+ {{ fsc.modelo303.casillaStr('02') }}
+ |
+ 03 |
+
+ {{ fsc.modelo303.casillaStr('03') }}
+ |
+
+
+ | 153 |
+
+ {{ fsc.modelo303.casillaStr('153') }}
+ |
+ 154 |
+
+ {{ fsc.modelo303.casillaStr('154') }}
+ |
+ 155 |
+
+ {{ fsc.modelo303.casillaStr('155') }}
+ |
+
+
+ | 04 |
+
+ {{ fsc.modelo303.casillaStr('04') }}
+ |
+ 05 |
+
+ {{ fsc.modelo303.casillaStr('05') }}
+ |
+ 06 |
+
+ {{ fsc.modelo303.casillaStr('06') }}
+ |
+
+
+ | 07 |
+
+ {{ fsc.modelo303.casillaStr('07') }}
+ |
+ 08 |
+
+ {{ fsc.modelo303.casillaStr('08') }}
+ |
+ 09 |
+
+ {{ fsc.modelo303.casillaStr('09') }}
+ |
+
+
+ | 10 |
+
+ {{ fsc.modelo303.casillaStr('10') }}
+ |
+ |
+ 11 |
+
+ {{ fsc.modelo303.casillaStr('11') }}
+ |
+
+
+ | 12 |
+
+ {{ fsc.modelo303.casillaStr('12') }}
+ |
+ |
+ 13 |
+
+ {{ fsc.modelo303.casillaStr('13') }}
+ |
+
+
+ | 14 |
+
+ {{ fsc.modelo303.casillaStr('14') }}
+ |
+ |
+ 15 |
+
+ {{ fsc.modelo303.casillaStr('15') }}
+ |
+
+
+ | 156 |
+
+ {{ fsc.modelo303.casillaStr('156') }}
+ |
+ 157 |
+
+ {{ fsc.modelo303.casillaStr('157') }}
+ |
+ 158 |
+
+ {{ fsc.modelo303.casillaStr('158') }}
+ |
+
+
+ | 168 |
+
+ {{ fsc.modelo303.casillaStr('168') }}
+ |
+ 169 |
+
+ {{ fsc.modelo303.casillaStr('169') }}
+ |
+ 170 |
+
+ {{ fsc.modelo303.casillaStr('170') }}
+ |
+
+
+ | 16 |
+
+ {{ fsc.modelo303.casillaStr('16') }}
+ |
+ 17 |
+
+ {{ fsc.modelo303.casillaStr('17') }}
+ |
+ 18 |
+
+ {{ fsc.modelo303.casillaStr('18') }}
+ |
+
+
+ | 19 |
+
+ {{ fsc.modelo303.casillaStr('19') }}
+ |
+ 20 |
+
+ {{ fsc.modelo303.casillaStr('20') }}
+ |
+ 21 |
+
+ {{ fsc.modelo303.casillaStr('21') }}
+ |
+
+
+ | 22 |
+
+ {{ fsc.modelo303.casillaStr('22') }}
+ |
+ 23 |
+
+ {{ fsc.modelo303.casillaStr('23') }}
+ |
+ 24 |
+
+ {{ fsc.modelo303.casillaStr('24') }}
+ |
+
+
+ | 25 |
+
+ {{ fsc.modelo303.casillaStr('25') }}
+ |
+ |
+ 26 |
+
+ {{ fsc.modelo303.casillaStr('26') }}
+ |
+
+
+
+
+
+
+ | {{ trans('total-accrued-fee') }} |
+ 27 |
+
+ {{ fsc.modelo303.casillaStr('27') }}
+ |
+
+
+
+
+
+
+
+
+
+
+
+ |
+ |
+ {{ trans('base') }} |
+ |
+ {{ trans('quota') }} |
+
+
+
+
+ | {{ trans('deductible-vat-28') }} |
+ 28 |
+
+ {{ fsc.modelo303.casillaStr('28') }}
+ |
+ 29 |
+
+ {{ fsc.modelo303.casillaStr('29') }}
+ |
+
+
+ | {{ trans('deductible-vat-32') }} |
+ 32 |
+
+ {{ fsc.modelo303.casillaStr('32') }}
+ |
+ 33 |
+
+ {{ fsc.modelo303.casillaStr('33') }}
+ |
+
+
+ | {{ trans('deductible-vat-36') }} |
+ 36 |
+
+ {{ fsc.modelo303.casillaStr('36') }}
+ |
+ 37 |
+
+ {{ fsc.modelo303.casillaStr('37') }}
+ |
+
+
+
+
+
+
+ | {{ trans('total-to-deduct') }} |
+ 45 |
+
+ {{ fsc.modelo303.casillaStr('45') }}
+ |
+
+
+
+
+
+
+
+
+
+ | {{ trans('total-result-of-the-general-regime') }} |
+ 46 |
+
+ {{ fsc.modelo303.casillaStr('46') }}
+ |
+
+
+
+
+
+
+
+
+
+ |
+ |
+ {{ trans('tax-base') }} |
+
+
+
+
+ | {{ trans('intra-community-deliveries') }} |
+ 59 |
+
+ {{ fsc.modelo303.casillaStr('59') }}
+ |
+
+
+ | {{ trans('exports') }} |
+ 60 |
+
+ {{ fsc.modelo303.casillaStr('60') }}
+ |
+
+
+ | {{ trans('sales-reverse-charge') }} |
+ 122 |
+
+ {{ fsc.modelo303.casillaStr('122') }}
+ |
+
+
+
+
+
+
\ No newline at end of file