├── .gitignore
├── stubs
├── images
│ └── logo.png
├── templates
│ ├── assets
│ │ └── style.css
│ ├── voided.html.twig
│ ├── summary.html.twig
│ ├── invoice2.html.twig
│ ├── retention.html.twig
│ ├── perception.html.twig
│ ├── despatch.html.twig
│ └── invoice.html.twig
└── certificate.pem
├── src
├── Contracts
│ ├── SenderInterface.php
│ └── DocumentBuilderInterface.php
├── Facades
│ ├── Greenter.php
│ └── GreenterReport.php
├── Builders
│ ├── Sale
│ │ ├── LegendBuilder.php
│ │ ├── DocumentBuilder.php
│ │ ├── PaymentTermsBuilder.php
│ │ ├── PrepaymentBuilder.php
│ │ ├── ChargeBuilder.php
│ │ ├── CuotaBuilder.php
│ │ ├── SalePerceptionBuilder.php
│ │ ├── DetractionBuilder.php
│ │ ├── DetailAttributeBuilder.php
│ │ ├── EmbededDespatchBuilder.php
│ │ ├── SaleDetailBuilder.php
│ │ ├── NoteBuilder.php
│ │ └── InvoiceBuilder.php
│ ├── Despatch
│ │ ├── PuertoBuilder.php
│ │ ├── DirectionBuilder.php
│ │ ├── AdditionalDocBuilder.php
│ │ ├── DriverBuilder.php
│ │ ├── TransportistBuilder.php
│ │ ├── VehiculoBuilder.php
│ │ ├── DespatchDetailBuilder.php
│ │ ├── ShipmentBuilder.php
│ │ └── DespatchBuilder.php
│ ├── Summary
│ │ ├── SummaryPerceptionBuilder.php
│ │ ├── SummaryBuilder.php
│ │ └── SummaryDetailBuilder.php
│ ├── Voided
│ │ ├── VoidedDetailBuilder.php
│ │ └── VoidedBuilder.php
│ ├── Retention
│ │ ├── PaymentBuilder.php
│ │ ├── ExchangeBuilder.php
│ │ ├── RetentionDetailBuilder.php
│ │ └── RetentionBuilder.php
│ ├── Company
│ │ ├── CompanyBuilder.php
│ │ └── AddressBuilder.php
│ ├── Client
│ │ └── ClientBuilder.php
│ └── Perception
│ │ ├── PerceptionDetailBuilder.php
│ │ └── PerceptionBuilder.php
├── Exceptions
│ └── GreenterException.php
├── Models
│ ├── XmlSigned.php
│ └── SunatResponse.php
├── Factories
│ ├── SenderFactory.php
│ ├── XmlBuilderFactory.php
│ └── DocumentBuilderFactory.php
├── GreenterServiceProvider.php
├── Senders
│ ├── ApiBuilder.php
│ └── SeeBuilder.php
└── Services
│ ├── ReportService.php
│ └── SenderService.php
├── composer.json
├── config
└── greenter.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.lock
--------------------------------------------------------------------------------
/stubs/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codersfree/laravel-greenter/HEAD/stubs/images/logo.png
--------------------------------------------------------------------------------
/src/Contracts/SenderInterface.php:
--------------------------------------------------------------------------------
1 | setCode($data['code'] ?? null)
13 | ->setValue($data['value'] ?? null);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Builders/Despatch/PuertoBuilder.php:
--------------------------------------------------------------------------------
1 | setCodigo($data['codigo'] ?? null)
13 | ->setNombre($data['nombre'] ?? null);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/DocumentBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDoc($data['tipoDoc'] ?? null)
13 | ->setNroDoc($data['nroDoc'] ?? null);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Builders/Despatch/DirectionBuilder.php:
--------------------------------------------------------------------------------
1 | setCodLocal($data['codLocal'] ?? null)
13 | ->setRuc($data['ruc'] ?? null);
14 | }
15 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/PaymentTermsBuilder.php:
--------------------------------------------------------------------------------
1 | setMoneda($data['moneda'] ?? null)
13 | ->setTipo($data['tipo'] ?? null)
14 | ->setMonto($data['monto'] ?? null);
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/PrepaymentBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDocRel($data['tipoDocRel'] ?? null)
13 | ->setNroDocRel($data['nroDocRel'] ?? null)
14 | ->setTotal($data['total'] ?? null);
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/ChargeBuilder.php:
--------------------------------------------------------------------------------
1 | setCodTipo($data['codTipo'] ?? null)
13 | ->setFactor($data['factor'] ?? null)
14 | ->setMonto($data['monto'] ?? null)
15 | ->setMontoBase($data['montoBase'] ?? null);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Builders/Despatch/AdditionalDocBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDesc($data['tipoDesc'] ?? null)
13 | ->setTipo($data['tipo'] ?? null)
14 | ->setNro($data['nro'] ?? null)
15 | ->setEmisor($data['emisor'] ?? null);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Exceptions/GreenterException.php:
--------------------------------------------------------------------------------
1 | setCodReg($data['codReg'] ?? null)
13 | ->setTasa($data['tasa'] ?? null)
14 | ->setMtoBase($data['mtoBase'] ?? null)
15 | ->setMtoTotal($data['mtoTotal'] ?? null);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Builders/Voided/VoidedDetailBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDoc($data['tipoDoc'] ?? null)
13 | ->setSerie($data['serie'] ?? null)
14 | ->setCorrelativo($data['correlativo'] ?? null)
15 | ->setDesMotivoBaja($data['desMotivoBaja'] ?? null);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/CuotaBuilder.php:
--------------------------------------------------------------------------------
1 | setMoneda($data['moneda'] ?? null)
13 | ->setMonto($data['monto'] ?? null)
14 | ->setFechaPago(
15 | isset($data['fechaPago'])
16 | ? new \DateTime($data['fechaPago'])
17 | : null
18 | );
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Builders/Retention/PaymentBuilder.php:
--------------------------------------------------------------------------------
1 | setMoneda($data['moneda'] ?? null)
13 | ->setImporte($data['importe'] ?? null)
14 | ->setFecha(
15 | isset($data['fecha'])
16 | ? new \DateTime($data['fecha'])
17 | : new \DateTime()
18 | );
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/SalePerceptionBuilder.php:
--------------------------------------------------------------------------------
1 | setCodReg($data['codReg'] ?? null)
13 | ->setPorcentaje($data['porcentaje'] ?? null)
14 | ->setMtoBase($data['mtoBase'] ?? null)
15 | ->setMto($data['mto'] ?? null)
16 | ->setMtoTotal($data['mtoTotal'] ?? null);
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Builders/Despatch/DriverBuilder.php:
--------------------------------------------------------------------------------
1 | setTipo($data['tipo'] ?? null)
13 | ->setTipoDoc($data['tipoDoc'] ?? null)
14 | ->setNroDoc($data['nroDoc'] ?? null)
15 | ->setNombres($data['nombres'] ?? null)
16 | ->setApellidos($data['apellidos'] ?? null)
17 | ->setLicencia($data['licencia'] ?? null);
18 | }
19 | }
--------------------------------------------------------------------------------
/src/Models/XmlSigned.php:
--------------------------------------------------------------------------------
1 | type;
18 | }
19 |
20 | public function getDocument(): DocumentInterface
21 | {
22 | return $this->document;
23 | }
24 |
25 | public function getXml(): string
26 | {
27 | return $this->xml;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/Builders/Retention/ExchangeBuilder.php:
--------------------------------------------------------------------------------
1 | setMonedaRef($data['monedaRef'] ?? null)
13 | ->setMonedaObj($data['monedaObj'] ?? null)
14 | ->setFactor($data['factor'] ?? null)
15 | ->setFecha(
16 | isset($data['fecha'])
17 | ? new \DateTime($data['fecha'])
18 | : new \DateTime()
19 | );
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/DetractionBuilder.php:
--------------------------------------------------------------------------------
1 | setPercent($data['percent'] ?? null)
13 | ->setMount($data['mount'] ?? null)
14 | ->setCtaBanco($data['ctaBanco'] ?? null)
15 | ->setCodMedioPago($data['codMedioPago'] ?? null)
16 | ->setCodBienDetraccion($data['codBienDetraccion'] ?? null)
17 | ->setValueRef($data['valueRef'] ?? null)
18 | ;
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Builders/Despatch/TransportistBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDoc($data['tipoDoc'] ?? null)
13 | ->setNumDoc($data['numDoc'] ?? null)
14 | ->setRznSocial($data['rznSocial'] ?? null)
15 | ->setNroMtc($data['nroMtc'] ?? null)
16 | ->setPlaca($data['placa'] ?? null)
17 | ->setChoferTipoDoc($data['choferTipoDoc'] ?? null)
18 | ->setChoferDoc($data['choferDoc'] ?? null);
19 | }
20 | }
--------------------------------------------------------------------------------
/src/Builders/Company/CompanyBuilder.php:
--------------------------------------------------------------------------------
1 | setRuc($company['ruc'] ?? null)
15 | ->setRazonSocial($company['razonSocial'] ?? null)
16 | ->setNombreComercial($company['nombreComercial'] ?? null)
17 | ->setAddress(
18 | isset($company['address'])
19 | ? (new AddressBuilder())->build($company['address'])
20 | : null
21 | );
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Builders/Company/AddressBuilder.php:
--------------------------------------------------------------------------------
1 | setUbigueo($data['ubigeo'] ?? null)
13 | ->setCodigoPais($data['codigoPais'] ?? 'PE')
14 | ->setDepartamento($data['departamento'] ?? null)
15 | ->setProvincia($data['provincia'] ?? null)
16 | ->setDistrito($data['distrito'] ?? null)
17 | ->setUrbanizacion($data['urbanizacion'] ?? null)
18 | ->setDireccion($data['direccion'] ?? null)
19 | ->setCodLocal($data['codLocal'] ?? '0000');
20 | }
21 | }
--------------------------------------------------------------------------------
/src/Builders/Despatch/VehiculoBuilder.php:
--------------------------------------------------------------------------------
1 | setPlaca($data['placa'] ?? null)
13 | ->setNroCirculacion($data['nroCirculacion'] ?? null)
14 | ->setCodEmisor($data['codEmisor'] ?? null)
15 | ->setNroAutorizacion($data['nroAutorizacion'] ?? null)
16 | ->setSecundarios(
17 | array_map(function ($secundario) {
18 | return (new VehiculoBuilder())->build($secundario);
19 | }, $data['secundarios'] ?? [])
20 | );
21 | }
22 | }
--------------------------------------------------------------------------------
/src/Builders/Client/ClientBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDoc($data['tipoDoc'] ?? null)
14 | ->setNumDoc($data['numDoc'] ?? '-')
15 | ->setRznSocial($data['rznSocial'] ?? null)
16 | ->setAddress(
17 | isset($data['address'])
18 | ? (new AddressBuilder())->build($data['address'])
19 | : null
20 | )
21 | ->setEmail($data['email'] ?? null)
22 | ->setTelephone($data['telephone'] ?? null);
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/DetailAttributeBuilder.php:
--------------------------------------------------------------------------------
1 | setCode($data['code'] ?? null)
13 | ->setName($data['name'] ?? null)
14 | ->setValue($data['value'] ?? null)
15 | ->setFecInicio(
16 | isset($data['fecInicio'])
17 | ? new \DateTime($data['fecInicio'])
18 | : null
19 | )
20 | ->setFecFin(
21 | isset($data['fecFin'])
22 | ? new \DateTime($data['fecFin'])
23 | : null
24 | )
25 | ->setDuracion($data['duracion'] ?? null);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Builders/Despatch/DespatchDetailBuilder.php:
--------------------------------------------------------------------------------
1 | setCodigo($data['codigo'] ?? null)
14 | ->setDescripcion($data['descripcion'] ?? null)
15 | ->setUnidad($data['unidad'] ?? null)
16 | ->setCantidad($data['cantidad'] ?? null)
17 | ->setCodProdSunat($data['codProdSunat'] ?? null)
18 | ->setAtributos(
19 | array_map(function ($attribute) {
20 | return (new DetailAttributeBuilder())->build($attribute);
21 | }, $data['atributos'] ?? [])
22 | );
23 | }
24 | }
--------------------------------------------------------------------------------
/src/Factories/SenderFactory.php:
--------------------------------------------------------------------------------
1 | new ApiBuilder(),
22 |
23 | // Para documentos electrónicos
24 | 'invoice',
25 | 'note',
26 | 'perception',
27 | 'retention',
28 | 'summary',
29 | 'voided' => new SeeBuilder($type),
30 | default => throw new \InvalidArgumentException("Sender type not supported: $type"),
31 | };
32 | }
33 | }
--------------------------------------------------------------------------------
/src/Factories/XmlBuilderFactory.php:
--------------------------------------------------------------------------------
1 | new DespatchBuilder(),
25 | 'invoice' => new InvoiceBuilder(),
26 | 'note' => new NoteBuilder(),
27 | 'perception' => new PerceptionBuilder(),
28 | 'retention' => new RetentionBuilder(),
29 | 'summary' => new SummaryBuilder(),
30 | 'voided' => new VoidedBuilder(),
31 | default => throw new \InvalidArgumentException("XML Builder type not supported: $type"),
32 | };
33 | }
34 | }
--------------------------------------------------------------------------------
/src/Builders/Voided/VoidedBuilder.php:
--------------------------------------------------------------------------------
1 | setCorrelativo($data['correlativo'] ?? null)
15 | ->setFecGeneracion(
16 | isset($data['fecGeneracion'])
17 | ? new \DateTime($data['fecGeneracion'])
18 | : null
19 | )
20 | ->setFecComunicacion(
21 | isset($data['fecComunicacion'])
22 | ? new \DateTime($data['fecComunicacion'])
23 | : null
24 | )
25 | ->setCompany(
26 | (new CompanyBuilder())->build()
27 | )
28 | ->setDetails(
29 | array_map(
30 | fn($detail) => (new VoidedDetailBuilder())->build($detail),
31 | $data['details'] ?? []
32 | )
33 | );
34 | }
35 | }
--------------------------------------------------------------------------------
/src/Builders/Summary/SummaryBuilder.php:
--------------------------------------------------------------------------------
1 | setCorrelativo($data['correlativo'] ?? null)
15 | ->setFecGeneracion(
16 | isset($data['fecGeneracion'])
17 | ? new \DateTime($data['fecGeneracion'])
18 | : null
19 | )
20 | ->setFecResumen(
21 | isset($data['fecResumen'])
22 | ? new \DateTime($data['fecResumen'])
23 | : null
24 | )
25 | ->setMoneda($data['moneda'] ?? 'PEN')
26 | ->setCompany(
27 | (new CompanyBuilder())->build()
28 | )
29 | ->setDetails(
30 | array_map(
31 | fn($detail) => (new SummaryDetailBuilder())->build($detail),
32 | $data['details'] ?? []
33 | )
34 | );
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Factories/DocumentBuilderFactory.php:
--------------------------------------------------------------------------------
1 | new DespatchBuilder(),
21 | 'invoice' => new InvoiceBuilder(),
22 | 'note' => new NoteBuilder(),
23 | 'perception' => new PerceptionBuilder(),
24 | 'retention' => new RetentionBuilder(),
25 | 'summary' => new SummaryBuilder(),
26 | 'voided' => new VoidedBuilder(),
27 | default => throw new GreenterException("Tipo de documento no soportado"),
28 | };
29 | }
30 | }
--------------------------------------------------------------------------------
/src/Models/SunatResponse.php:
--------------------------------------------------------------------------------
1 | document;
21 | }
22 |
23 | public function getCdrZip(): ?string
24 | {
25 | return $this->cdrZip;
26 | }
27 |
28 | public function getCdrResponse(): CdrResponse
29 | {
30 | return $this->cdrResponse;
31 | }
32 |
33 | public function getXml(): string
34 | {
35 | return $this->xml;
36 | }
37 |
38 | public function getHash(): string
39 | {
40 | return (new XmlUtils())->getHashSign($this->xml);
41 | }
42 |
43 | public function readCdr(): array
44 | {
45 | return [
46 | 'id' => $this->cdrResponse->getId(),
47 | 'code' => (int) $this->cdrResponse->getCode(),
48 | 'description' => $this->cdrResponse->getDescription(),
49 | 'notes' => $this->cdrResponse->getNotes(),
50 | 'reference' => $this->cdrResponse->getReference(),
51 | ];
52 | }
53 | }
--------------------------------------------------------------------------------
/src/Builders/Sale/EmbededDespatchBuilder.php:
--------------------------------------------------------------------------------
1 | setLlegada(
15 | isset($data['llegada'])
16 | ? (new DirectionBuilder())->build($data['llegada'])
17 | : null
18 | )
19 | ->setPartida(
20 | isset($data['partida'])
21 | ? (new DirectionBuilder())->build($data['partida'])
22 | : null
23 | )
24 | ->setTransportista(
25 | isset($data['transportista'])
26 | ? (new ClientBuilder())->build($data['transportista'])
27 | : null
28 | )
29 | ->setNroLicencia($data['nroLicencia'] ?? null)
30 | ->setTranspPlaca($data['transpPlaca'] ?? null)
31 | ->setTranspCodeAuth($data['transpCodeAuth'] ?? null)
32 | ->setTranspMarca($data['transpMarca'] ?? null)
33 | ->setModTraslado($data['modTraslado'] ?? null)
34 | ->setPesoBruto($data['pesoBruto'] ?? null)
35 | ->setUndPesoBruto($data['undPesoBruto'] ?? null);
36 | }
37 | }
--------------------------------------------------------------------------------
/src/Builders/Retention/RetentionDetailBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDoc($data['tipoDoc'] ?? null)
13 | ->setNumDoc($data['numDoc'] ?? null)
14 | ->setFechaEmision(
15 | isset($data['fechaEmision'])
16 | ? new \DateTime($data['fechaEmision'])
17 | : null
18 | )
19 | ->setImpTotal($data['impTotal'] ?? null)
20 | ->setMoneda($data['moneda'] ?? null)
21 | ->setPagos(
22 | array_map(
23 | fn($pago) => (new PaymentBuilder())->build($pago),
24 | $data['pagos'] ?? []
25 | )
26 | )
27 | ->setFechaRetencion(
28 | isset($data['fechaRetencion'])
29 | ? new \DateTime($data['fechaRetencion'])
30 | : null
31 | )
32 | ->setImpRetenido($data['impRetenido'] ?? null)
33 | ->setImpPagar($data['impPagar'] ?? null)
34 | ->setTipoCambio(
35 | isset($data['tipoCambio'])
36 | ? (new ExchangeBuilder())->build($data['tipoCambio'])
37 | : null
38 | );
39 | }
40 | }
--------------------------------------------------------------------------------
/src/Builders/Retention/RetentionBuilder.php:
--------------------------------------------------------------------------------
1 | setSerie($data['serie'] ?? null)
16 | ->setCorrelativo($data['correlativo'] ?? null)
17 | ->setFechaEmision(
18 | isset($data['fechaEmision'])
19 | ? new \DateTime($data['fechaEmision'])
20 | : null
21 | )
22 | ->setCompany(
23 | (new CompanyBuilder())->build()
24 | )
25 | ->setProveedor(
26 | isset($data['proveedor'])
27 | ? (new ClientBuilder())->build($data['proveedor'])
28 | : null
29 | )
30 | ->setRegimen($data['regimen'] ?? null)
31 | ->setTasa($data['tasa'] ?? null)
32 | ->setImpRetenido($data['impRetenido'] ?? null)
33 | ->setImpPagado($data['impPagado'] ?? null)
34 | ->setObservacion($data['observacion'] ?? null)
35 | ->setDetails(
36 | array_map(
37 | fn($detail) => (new RetentionDetailBuilder())->build($detail),
38 | $data['details'] ?? []
39 | )
40 | )
41 | ;
42 | }
43 | }
--------------------------------------------------------------------------------
/src/Builders/Perception/PerceptionDetailBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDoc($data['tipoDoc'] ?? null)
15 | ->setNumDoc($data['numDoc'] ?? null)
16 | ->setFechaEmision(
17 | isset($data['fechaEmision'])
18 | ? new \DateTime($data['fechaEmision'])
19 | : new \DateTime()
20 | )
21 | ->setImpTotal($data['impTotal'] ?? null)
22 | ->setMoneda($data['moneda'] ?? null)
23 | ->setCobros(
24 | array_map(
25 | function ($cobro) {
26 | return (new PaymentBuilder())->build($cobro);
27 | },
28 | $data['cobros'] ?? []
29 | )
30 | )
31 | ->setFechaPercepcion(
32 | isset($data['fechaPercepcion'])
33 | ? new \DateTime($data['fechaPercepcion'])
34 | : new \DateTime()
35 | )
36 | ->setImpPercibido($data['impPercibido'] ?? null)
37 | ->setImpCobrar($data['impCobrar'] ?? null)
38 | ->setTipoCambio(
39 | isset($data['tipoCambio'])
40 | ? (new ExchangeBuilder())->build($data['tipoCambio'])
41 | : null
42 | );
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Builders/Perception/PerceptionBuilder.php:
--------------------------------------------------------------------------------
1 | setSerie($data['serie'] ?? null)
16 | ->setCorrelativo($data['correlativo'] ?? null)
17 | ->setFechaEmision(
18 | isset($data['fechaEmision'])
19 | ? new \DateTime($data['fechaEmision'])
20 | : new \DateTime()
21 | )
22 | ->setCompany(
23 | (new CompanyBuilder())->build()
24 | )
25 | ->setProveedor(
26 | isset($data['proveedor'])
27 | ? (new ClientBuilder())->build($data['proveedor'])
28 | : null
29 | )
30 | ->setRegimen($data['regimen'] ?? null)
31 | ->setTasa($data['tasa'] ?? null)
32 | ->setImpPercibido($data['impPercibido'] ?? null)
33 | ->setImpCobrado($data['impCobrado'] ?? null)
34 | ->setObservacion($data['observacion'] ?? null)
35 | ->setDetails(
36 | array_map(
37 | function ($detail) {
38 | return (new PerceptionDetailBuilder())->build($detail);
39 | },
40 | $data['details'] ?? []
41 | )
42 | );
43 | }
44 | }
--------------------------------------------------------------------------------
/src/GreenterServiceProvider.php:
--------------------------------------------------------------------------------
1 | mergeConfigFrom(__DIR__.'/../config/greenter.php', 'greenter');
16 |
17 | $this->app->singleton('greenter', function ($app) {
18 | return new SenderService();
19 | });
20 |
21 | $this->app->singleton('greenter.report', function ($app) {
22 | return new ReportService();
23 | });
24 | }
25 |
26 | public function boot()
27 | {
28 | $this->publishes([
29 | __DIR__.'/../stubs/images/logo.png' => public_path('images/logo.png'),
30 | ], 'greenter-logo');
31 |
32 | $this->publishes([
33 | __DIR__.'/../stubs/templates' => config('greenter.report.templates'),
34 | ], 'greenter-templates');
35 |
36 | $this->publishes([
37 | __DIR__.'/../stubs/certificate.pem' => public_path('certs/certificate.pem'),
38 | ], 'greenter-certificate');
39 |
40 | $this->publishes([
41 | __DIR__.'/../config/greenter.php' => config_path('greenter.php'),
42 | ], 'greenter-config');
43 |
44 | $this->publishes([
45 | __DIR__.'/../stubs/images/logo.png' => public_path('images/logo.png'),
46 | __DIR__.'/../stubs/certificate.pem' => public_path('certs/certificate.pem'),
47 | __DIR__.'/../config/greenter.php' => config_path('greenter.php'),
48 | ], 'greenter-laravel');
49 |
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Builders/Summary/SummaryDetailBuilder.php:
--------------------------------------------------------------------------------
1 | setTipoDoc($data['tipoDoc'] ?? null)
14 | ->setSerieNro($data['serieNro'] ?? null)
15 | ->setClienteTipo($data['clienteTipo'] ?? null)
16 | ->setClienteNro($data['clienteNro'] ?? null)
17 | ->setDocReferencia(
18 | isset($data['docReferencia'])
19 | ? (new DocumentBuilder())->build($data['docReferencia'])
20 | : null
21 | )
22 | ->setPercepcion(
23 | isset($data['percepcion'])
24 | ? (new SummaryPerceptionBuilder())->build($data['percepcion'])
25 | : null
26 | )
27 | ->setEstado($data['estado'] ?? null)
28 | ->setTotal($data['total'] ?? null)
29 | ->setMtoOperGravadas($data['mtoOperGravadas'] ?? null)
30 | ->setMtoOperInafectas($data['mtoOperInafectas'] ?? null)
31 | ->setMtoOperExoneradas($data['mtoOperExoneradas'] ?? null)
32 | ->setMtoOperExportacion($data['mtoOperExportacion'] ?? null)
33 | ->setMtoOperGratuitas($data['mtoOperGratuitas'] ?? null)
34 | ->setMtoOtrosCargos($data['mtoOtrosCargos'] ?? null)
35 | ->setMtoIGV($data['mtoIGV'] ?? null)
36 | ->setMtoIvap($data['mtoIvap'] ?? null)
37 | ->setMtoISC($data['mtoISC'] ?? null)
38 | ->setMtoOtrosTributos($data['mtoOtrosTributos'] ?? null)
39 | ->setMtoIcbper($data['mtoIcbper'] ?? null);
40 | }
41 | }
--------------------------------------------------------------------------------
/src/Senders/ApiBuilder.php:
--------------------------------------------------------------------------------
1 | setBuilderOptions([
29 | 'strict_variables' => true,
30 | 'optimizations' => 0,
31 | 'debug' => true,
32 | 'cache' => false,
33 | ]);
34 |
35 | $api->setApiCredentials(
36 | $mode === 'prod'
37 | ? $company['credentials']['client_id']
38 | : 'test-85e5b0ae-255c-4891-a595-0b98c65c9854',
39 | $mode === 'prod'
40 | ? $company['credentials']['client_secret']
41 | : 'test-Hty/M6QshYvPgItX2P0+Kw=='
42 | );
43 |
44 | $api->setClaveSOL(
45 | $company['ruc'],
46 | $mode === 'prod'
47 | ? $company['clave_sol']['user']
48 | : 'MODDATOS',
49 | $mode === 'prod'
50 | ? $company['clave_sol']['password']
51 | : 'MODDATOS'
52 | );
53 |
54 | $api->setCertificate(
55 | file_get_contents($company['certificate'])
56 | );
57 |
58 | return $api;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Senders/SeeBuilder.php:
--------------------------------------------------------------------------------
1 | setCertificate(
30 | file_get_contents($certPath)
31 | );
32 | $see->setService($this->getEndpoint());
33 | $see->setClaveSOL(
34 | $company['ruc'],
35 | $company['clave_sol']['user'],
36 | $company['clave_sol']['password']
37 | );
38 |
39 | return $see;
40 | }
41 |
42 | public function getEndpoint(): string
43 | {
44 | $mode = config('greenter.mode');
45 | $endpoints = config('greenter.endpoints');
46 |
47 | return match ($this->type) {
48 | 'invoice',
49 | 'note',
50 | 'voided',
51 | 'summary' => $mode === 'prod'
52 | ? $endpoints['fe']['prod']
53 | : $endpoints['fe']['beta'],
54 |
55 | 'perception',
56 | 'retention'=> $mode === 'prod'
57 | ? $endpoints['retencion']['prod']
58 | : $endpoints['retencion']['beta'],
59 |
60 | default => throw new \InvalidArgumentException("Tipo de documento no soportado: $this->type"),
61 | };
62 |
63 |
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codersfree/laravel-greenter",
3 | "description": "Laravel package for Greenter",
4 | "type": "laravel-package",
5 | "license": "MIT",
6 | "keywords": [
7 | "laravel",
8 | "greenter",
9 | "facturacion",
10 | "sunat",
11 | "pdf",
12 | "boletas",
13 | "facturas"
14 | ],
15 | "homepage": "https://github.com/codersfree/laravel-greenter",
16 | "support": {
17 | "issues": "https://github.com/codersfree/laravel-greenter/issues",
18 | "source": "https://github.com/codersfree/laravel-greenter"
19 | },
20 | "authors": [
21 | {
22 | "name": "CodersFree",
23 | "email": "victor@codersfree.com"
24 | }
25 | ],
26 | "minimum-stability": "stable",
27 | "prefer-stable": true,
28 | "require": {
29 | "php": ">=8.1",
30 | "codersfree/report": "^1.0",
31 | "greenter/htmltopdf": "^5.1",
32 | "greenter/lite": "^5.1",
33 | "illuminate/console": "^10.0|^11.0|^12.0",
34 | "illuminate/contracts": "^10.0|^11.0|^12.0",
35 | "illuminate/filesystem": "^10.0|^11.0|^12.0",
36 | "illuminate/support": "^10.0|^11.0|^12.0",
37 | "illuminate/view": "^10.0|^11.0|^12.0"
38 | },
39 | "require-dev": {
40 | "phpunit/phpunit": "^10.0"
41 | },
42 | "suggest": {
43 | "laravel/telescope": "Recomendado para depuración durante el desarrollo"
44 | },
45 | "autoload": {
46 | "psr-4": {
47 | "CodersFree\\LaravelGreenter\\": "src/"
48 | }
49 | },
50 | "extra": {
51 | "laravel": {
52 | "providers": [
53 | "CodersFree\\LaravelGreenter\\GreenterServiceProvider"
54 | ],
55 | "aliases": {
56 | "Greenter": "CodersFree\\LaravelGreenter\\Facades\\Greenter",
57 | "GreenterDespatch": "CodersFree\\LaravelGreenter\\Facades\\GreenterDespatch",
58 | "GreenterReport": "CodersFree\\LaravelGreenter\\Facades\\GreenterReport"
59 | }
60 | }
61 | },
62 | "config": {
63 | "sort-packages": true
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/stubs/templates/assets/style.css:
--------------------------------------------------------------------------------
1 | .bold,b,strong{font-weight:700}body{background-repeat:no-repeat;background-position:center center;text-align:center;margin:0;font-family: Verdana, monospace} .tabla_borde{border:1px solid #666;border-radius:10px} tr.border_bottom td{border-bottom:1px solid #000} tr.border_top td{border-top:1px solid #666}td.border_right{border-right:1px solid #666}.table-valores-totales tbody>tr>td{border:0} .table-valores-totales>tbody>tr>td:first-child{text-align:right} .table-valores-totales>tbody>tr>td:last-child{border-bottom:1px solid #666;text-align:right;width:30%} hr,img{border:0} table td{font-size:12px} html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;font-size:10px;-webkit-tap-highlight-color:transparent} a{background-color:transparent} a:active,a:hover{outline:0} img{vertical-align:middle} hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;margin-top:20px;margin-bottom:20px;border-top:1px solid #eee} table{border-spacing:0;border-collapse:collapse}@media print{blockquote,img,tr{page-break-inside:avoid}*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}blockquote{border:1px solid #999}img{max-width:100%!important}p{orphans:3;widows:3}.table{border-collapse:collapse!important}.table td{background-color:#fff!important}} a,a:focus,a:hover{text-decoration:none} *,:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} a{color:#428bca;cursor:pointer} a:focus,a:hover{color:#2a6496} a:focus{outline:dotted thin;outline:-webkit-focus-ring-color auto 5px;outline-offset:-2px} h6{font-family:inherit;line-height:1.1;color:inherit;margin-top:10px;margin-bottom:10px} p{margin:0 0 10px} blockquote{padding:10px 20px;margin:0 0 20px;border-left:5px solid #eee} table{background-color:transparent} .table{width:100%;max-width:100%;margin-bottom:20px} h6{font-weight:100;font-size:10px} body{line-height:1.42857143;font-family:"open sans","Helvetica Neue",Helvetica,Arial,sans-serif;background-color:#2f4050;font-size:13px;color:#676a6c;overflow-x:hidden} .table>tbody>tr>td{vertical-align:top;border-top:1px solid #e7eaec;line-height:1.42857;padding:8px} .white-bg{background-color:#fff} td{padding:6} .table-valores-totales tbody>tr>td{border-top:0 none!important}
--------------------------------------------------------------------------------
/src/Builders/Sale/SaleDetailBuilder.php:
--------------------------------------------------------------------------------
1 | setUnidad($data['unidad'] ?? null)
13 | ->setCantidad($data['cantidad'] ?? null)
14 | ->setCodProducto($data['codProducto'] ?? null)
15 | ->setCodProdSunat($data['codProdSunat'] ?? null)
16 | ->setCodProdGS1($data['codProdGS1'] ?? null)
17 | ->setDescripcion($data['descripcion'] ?? null)
18 | ->setMtoValorUnitario($data['mtoValorUnitario'] ?? null)
19 | ->setCargos(
20 | array_map(function ($cargo) {
21 | return (new ChargeBuilder())->build($cargo);
22 | }, $data['cargos'] ?? [])
23 | )
24 | ->setDescuentos(
25 | array_map(function ($descuento) {
26 | return (new ChargeBuilder())->build($descuento);
27 | }, $data['descuentos'] ?? [])
28 | )
29 | ->setDescuento($data['descuento'] ?? null)
30 | ->setMtoBaseIgv($data['mtoBaseIgv'] ?? null)
31 | ->setPorcentajeIgv($data['porcentajeIgv'] ?? null)
32 | ->setIgv($data['igv'] ?? null)
33 | ->setTipAfeIgv($data['tipAfeIgv'] ?? null)
34 | ->setMtoBaseIsc($data['mtoBaseIsc'] ?? null)
35 | ->setPorcentajeIsc($data['porcentajeIsc'] ?? null)
36 | ->setIsc($data['isc'] ?? null)
37 | ->setTipSisIsc($data['tipSisIsc'] ?? null)
38 | ->setMtoBaseOth($data['mtoBaseOth'] ?? null)
39 | ->setPorcentajeOth($data['porcentajeOth'] ?? null)
40 | ->setOtroTributo($data['otroTributo'] ?? null)
41 | ->setIcbper($data['icbper'] ?? null)
42 | ->setFactorIcbper($data['factorIcbper'] ?? null)
43 | ->setTotalImpuestos($data['totalImpuestos'] ?? null)
44 | ->setMtoPrecioUnitario($data['mtoPrecioUnitario'] ?? null)
45 | ->setMtoValorVenta($data['mtoValorVenta'] ?? null)
46 | ->setMtoValorGratuito($data['mtoValorGratuito'] ?? null)
47 | ->setAtributos(
48 | array_map(function ($atributo) {
49 | return (new DetailAttributeBuilder())->build($atributo);
50 | }, $data['atributos'] ?? [])
51 | )
52 | ;
53 | }
54 | }
--------------------------------------------------------------------------------
/src/Builders/Despatch/ShipmentBuilder.php:
--------------------------------------------------------------------------------
1 | setCodTraslado($data['codTraslado'] ?? null)
13 | ->setDesTraslado($data['desTraslado'] ?? null)
14 | ->setSustentoPeso($data['sustentoPeso'] ?? null)
15 | ->setIndTransbordo($data['indTransbordo'] ?? null)
16 | ->setIndicadores($data['indicadores'] ?? [])
17 | ->setPesoItems($data['pesoItems'] ?? null)
18 | ->setPesoTotal($data['pesoTotal'] ?? null)
19 | ->setUndPesoTotal($data['undPesoTotal'] ?? null)
20 | ->setNumBultos($data['numBultos'] ?? null)
21 | ->setModTraslado($data['modTraslado'] ?? null)
22 | ->setFecTraslado(
23 | isset($data['fecTraslado'])
24 | ? new \DateTime($data['fecTraslado'])
25 | : null
26 | )
27 | ->setNumContenedor($data['numContenedor'] ?? null)
28 | ->setContenedores($data['contenedores'] ?? [])
29 | ->setCodPuerto($data['codPuerto'] ?? null)
30 | ->setPuerto(
31 | isset($data['puerto'])
32 | ? (new PuertoBuilder())->build($data['puerto'])
33 | : null
34 | )
35 | ->setAeropuerto(
36 | isset($data['aeropuerto'])
37 | ? (new PuertoBuilder())->build($data['aeropuerto'])
38 | : null
39 | )
40 | ->setTransportista(
41 | isset($data['transportista'])
42 | ? (new TransportistBuilder())->build($data['transportista'])
43 | : null
44 | )
45 | ->setVehiculo(
46 | isset($data['vehiculo'])
47 | ? (new VehiculoBuilder())->build($data['vehiculo'])
48 | : null
49 | )
50 | ->setChoferes(
51 | array_map(function ($chofer) {
52 | return (new DriverBuilder())->build($chofer);
53 | }, $data['choferes'] ?? [])
54 | )
55 | ->setLlegada(
56 | isset($data['llegada'])
57 | ? (new DirectionBuilder())->build($data['llegada'])
58 | : null
59 | )
60 | ->setPartida(
61 | isset($data['partida'])
62 | ? (new DirectionBuilder())->build($data['partida'])
63 | : null
64 | );
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Services/ReportService.php:
--------------------------------------------------------------------------------
1 | $customParams]);
19 |
20 | return $this;
21 | }
22 |
23 | public function setOptions(array $options): self
24 | {
25 | $defaultOptions = config('greenter.report.options');
26 | $customOptions = array_replace_recursive($defaultOptions, $options);
27 |
28 | config(['greenter.report.options' => $customOptions]);
29 |
30 | return $this;
31 | }
32 |
33 | public function generateHtml(DocumentInterface $document)
34 | {
35 | $htmlReport = $this->createHtmlReport($document);
36 | return $htmlReport->render($document, $this->getParamsWithLogo());
37 | }
38 |
39 | public function generatePdf(DocumentInterface $document)
40 | {
41 | $htmlReport = $this->createHtmlReport($document);
42 |
43 | $pdfReport = new PdfReport($htmlReport);
44 | $pdfReport->setBinPath(config('greenter.report.bin_path'));
45 | $pdfReport->setOptions(config('greenter.report.options'));
46 |
47 | $pdf = $pdfReport->render($document, $this->getParamsWithLogo());
48 |
49 | if ($pdf === null) {
50 | throw new \RuntimeException($pdfReport->getExporter()->getError());
51 | }
52 |
53 | return $pdf;
54 | }
55 |
56 | public function createHtmlReport(DocumentInterface $document): HtmlReport
57 | {
58 | $templatePath = config('greenter.report.templates');
59 | $twigOptions = config('greenter.report.twigOptions');
60 |
61 | $htmlReport = File::isDirectory($templatePath)
62 | ? new HtmlReport($templatePath, $twigOptions)
63 | : new HtmlReport();
64 |
65 | $resolver = new DefaultTemplateResolver();
66 | $htmlReport->setTemplate($resolver->getTemplate($document));
67 |
68 | return $htmlReport;
69 | }
70 |
71 | protected function getParamsWithLogo(): array
72 | {
73 | $params = config('greenter.report.params');
74 |
75 | if (isset($params['system']['logo']) && file_exists($params['system']['logo'])) {
76 | $params['system']['logo'] = file_get_contents($params['system']['logo']);
77 | }
78 |
79 | return $params;
80 | }
81 | }
--------------------------------------------------------------------------------
/src/Builders/Despatch/DespatchBuilder.php:
--------------------------------------------------------------------------------
1 | setVersion($data['version'] ?? null)
17 | ->setTipoDoc($data['tipoDoc'] ?? null)
18 | ->setSerie($data['serie'] ?? null)
19 | ->setCorrelativo($data['correlativo'] ?? null)
20 | ->setObservacion($data['observacion'] ?? null)
21 | ->setFechaEmision(
22 | $data['fechaEmision']
23 | ? new \DateTime($data['fechaEmision'])
24 | : null
25 | )
26 | ->setCompany(
27 | (new CompanyBuilder())->build()
28 | )
29 | ->setDestinatario(
30 | isset($data['destinatario'])
31 | ? (new ClientBuilder())->build($data['destinatario'])
32 | : null
33 | )
34 | ->setTercero(
35 | isset($data['tercero'])
36 | ? (new ClientBuilder())->build($data['tercero'])
37 | : null
38 | )
39 | ->setComprador(
40 | isset($data['comprador'])
41 | ? (new ClientBuilder())->build($data['comprador'])
42 | : null
43 | )
44 | ->setEnvio(
45 | isset($data['envio'])
46 | ? (new ShipmentBuilder())->build($data['envio'])
47 | : null
48 | )
49 | ->setDocBaja(
50 | isset($data['docBaja'])
51 | ? (new DocumentBuilder())->build($data['docBaja'])
52 | : null
53 | )
54 | ->setRelDoc(
55 | isset($data['relDoc'])
56 | ? (new DocumentBuilder())->build($data['relDoc'])
57 | : null
58 | )
59 | ->setAddDocs(
60 | array_map(
61 | fn($doc) => (new AdditionalDocBuilder())->build($doc),
62 | $data['addDocs'] ?? []
63 | )
64 | )
65 | ->setDetails(
66 | array_map(
67 | fn($detail) => (new DespatchDetailBuilder())->build($detail),
68 | $data['details'] ?? []
69 | )
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/config/greenter.php:
--------------------------------------------------------------------------------
1 | env('GREENTER_MODE', 'beta'), // beta o prod
7 |
8 | 'company' => [
9 | 'ruc' => env('GREENTER_COMPANY_RUC', '20000000001'),
10 | 'razonSocial' => env('GREENTER_COMPANY_NAME', 'GREEN SAC'),
11 | 'nombreComercial' => env('GREENTER_COMPANY_COMMERCIAL_NAME', 'GREEN'),
12 |
13 | 'address' => [
14 | 'ubigeo' => env('GREENTER_COMPANY_UBIGEO', '150101'),
15 | 'departamento' => env('GREENTER_COMPANY_DEPARTMENT', 'LIMA'),
16 | 'provincia' => env('GREENTER_COMPANY_PROVINCE', 'LIMA'),
17 | 'distrito' => env('GREENTER_COMPANY_DISTRICT', 'LIMA'),
18 | 'direccion' => env('GREENTER_COMPANY_ADDRESS', 'Av. Villa Nueva 221'),
19 | ],
20 |
21 | 'certificate' => public_path('certs/certificate.pem'),
22 |
23 | 'clave_sol' => [
24 | 'user' => env('GREENTER_SOL_USER', 'MODDATOS'),
25 | 'password' => env('GREENTER_SOL_PASS', 'MODDATOS'),
26 | ],
27 |
28 | 'credentials' => [
29 | 'client_id' => env('GREENTER_CLIENT_ID', 'test-85e5b0ae-255c-4891-a595-0b98c65c9854'),
30 | 'client_secret' => env('GREENTER_CLIENT_SECRET', 'test-Hty/M6QshYvPgItX2P0+Kw=='),
31 | ],
32 | ],
33 |
34 | 'endpoints' => [
35 | 'fe' => [
36 | 'beta' => SunatEndpoints::FE_BETA,
37 | 'prod' => SunatEndpoints::FE_PRODUCCION,
38 | ],
39 | 'retencion' => [
40 | 'beta' => SunatEndpoints::RETENCION_BETA,
41 | 'prod' => SunatEndpoints::RETENCION_PRODUCCION,
42 | ],
43 | 'api' => [
44 | 'beta' => [
45 | 'auth' => 'https://gre-test.nubefact.com/v1',
46 | 'cpe' => 'https://gre-test.nubefact.com/v1',
47 | ],
48 | 'prod' => [
49 | 'auth' => 'https://api-seguridad.sunat.gob.pe/v1',
50 | 'cpe' => 'https://api-cpe.sunat.gob.pe/v1',
51 | ],
52 | ],
53 | ],
54 |
55 | 'report' => [
56 | 'params' => [
57 | 'system' => [
58 | 'logo' => env('GREENTER_COMPANY_LOGO', public_path('images/logo.png')),
59 | 'hash' => '',
60 | ],
61 | 'user' => [
62 | 'header' => env('GREENTER_COMPANY_HEADER', 'Telf: (01) 123456'),
63 | 'extras' => [
64 | ['name' => 'CONDICIÓN DE PAGO', 'value' => 'Contado'],
65 | ['name' => 'VENDEDOR', 'value' => 'VENDEDOR PRINCIPAL'],
66 | ],
67 | 'footer' => env('GREENTER_COMPANY_FOOTER', '
Nro Resolución: 123456789
'),
68 | ]
69 | ],
70 | 'twigOptions' => [
71 | /* 'cache' => storage_path('framework/cache/data/greenter/twig'), */
72 | 'strict_variables' => true,
73 | ],
74 | 'templates' => resource_path('views/vendor/laravel-greenter'),
75 | 'options' => [
76 | 'no-outline',
77 | 'viewport-size' => '1280x1024',
78 | 'page-width' => '21cm',
79 | 'page-height' => '29.7cm',
80 | ],
81 | 'bin_path' => env('GREENTER_PDF_BIN_PATH', 'C:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe'),
82 | ],
83 | ];
84 |
--------------------------------------------------------------------------------
/stubs/certificate.pem:
--------------------------------------------------------------------------------
1 | Bag Attributes
2 | localKeyID: 2A 08 48 DE A1 76 E7 4C 18 95 9D 65 06 B2 35 DB 19 F2 87 D5
3 | friendlyName: v54t03123..
4 | subject=DC = LLAMA.PE SA, C = PE, ST = LIMA, L = LIMA, O = TU EMPRESA S.A., OU = DNI 9999999 RUC 20609278235 - CERTIFICADO PARA DEMOSTRACI\C3\93N, CN = NOMBRE REPRESENTANTE LEGAL - CERTIFICADO PARA DEMOSTRACI\C3\93N, emailAddress = demo@llama.pe
5 |
6 | issuer=DC = LLAMA.PE SA, C = PE, ST = LIMA, L = LIMA, O = TU EMPRESA S.A., OU = DNI 9999999 RUC 20609278235 - CERTIFICADO PARA DEMOSTRACI\C3\93N, CN = NOMBRE REPRESENTANTE LEGAL - CERTIFICADO PARA DEMOSTRACI\C3\93N, emailAddress = demo@llama.pe
7 |
8 | -----BEGIN CERTIFICATE-----
9 | MIIFBzCCA++gAwIBAgIIV5TzCbsSyOswDQYJKoZIhvcNAQELBQAwggENMRswGQYK
10 | CZImiZPyLGQBGRYLTExBTUEuUEUgU0ExCzAJBgNVBAYTAlBFMQ0wCwYDVQQIDARM
11 | SU1BMQ0wCwYDVQQHDARMSU1BMRgwFgYDVQQKDA9UVSBFTVBSRVNBIFMuQS4xRTBD
12 | BgNVBAsMPEROSSA5OTk5OTk5IFJVQyAyMDYwOTI3ODIzNSAtIENFUlRJRklDQURP
13 | IFBBUkEgREVNT1NUUkFDScOTTjFEMEIGA1UEAww7Tk9NQlJFIFJFUFJFU0VOVEFO
14 | VEUgTEVHQUwgLSBDRVJUSUZJQ0FETyBQQVJBIERFTU9TVFJBQ0nDk04xHDAaBgkq
15 | hkiG9w0BCQEWDWRlbW9AbGxhbWEucGUwHhcNMjMxMDExMjA1MDI3WhcNMjUxMDEw
16 | MjA1MDI3WjCCAQ0xGzAZBgoJkiaJk/IsZAEZFgtMTEFNQS5QRSBTQTELMAkGA1UE
17 | BhMCUEUxDTALBgNVBAgMBExJTUExDTALBgNVBAcMBExJTUExGDAWBgNVBAoMD1RV
18 | IEVNUFJFU0EgUy5BLjFFMEMGA1UECww8RE5JIDk5OTk5OTkgUlVDIDIwNjA5Mjc4
19 | MjM1IC0gQ0VSVElGSUNBRE8gUEFSQSBERU1PU1RSQUNJw5NOMUQwQgYDVQQDDDtO
20 | T01CUkUgUkVQUkVTRU5UQU5URSBMRUdBTCAtIENFUlRJRklDQURPIFBBUkEgREVN
21 | T1NUUkFDScOTTjEcMBoGCSqGSIb3DQEJARYNZGVtb0BsbGFtYS5wZTCCASIwDQYJ
22 | KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMe23MwnavEvBEQoWzxrUZKm/OIeViMC
23 | 0mEqwQeqs31Uj4HiUfPZ8pbJd+lct1/dSdbfBVpbvusVDfNSIXubrWGkGgYB6DT+
24 | Kw0u0TvqBAr6vExSiJh7dWfuCsWq8ayLQOcPHULqiSf73cv1RTXRwRYtkKaz4lMF
25 | wDEL9jZ5JobW6QDgpPP86fIrXNlXptMYkbhcAB6dHpZWzWFIGhqLKxwiePsxFhYe
26 | uBTenDe4IC7DNaoZHZTOyR6u6Ch/A/zDLoNmVWNyo4CGaDWygUENHX4f1DiJSdJa
27 | L1UDQudbKs9TE9rzjzgRb6iO+GzPTXXmQCRpn7tieMcRwOuPSXW1sjECAwEAAaNn
28 | MGUwHQYDVR0OBBYEFBFg+uJamd4dCqUL2s4/3z0zzpJJMB8GA1UdIwQYMBaAFBFg
29 | +uJamd4dCqUL2s4/3z0zzpJJMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA4GA1UdDwEB
30 | /wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAY8IFByC6tYu9tCrjDtOE8tjQ0H4x
31 | Uf7dRbUT8EYhcQDEVFAJz/NsKvnp8E6uLBd3H7NknMLkQ7M2+1reo+Ghxs0URBKu
32 | c2TIwjEWZfWPUbT2cYXwACqXEihcRhOZK3Po6mbzxAvPjENfcjkJJSOh1rtL9Spd
33 | KYc7uKq5N29rvq4vcMs3WuIH08jlyUvNbMfc6dzinUaopHC180KFr8qL3p659Swl
34 | ntVV9WIlh/A4HPN+kWjvu9+wsCCFlpc4V4SknIut0bMT/4ZZUb1Gvw5GNA0vEKQk
35 | VfR0Rm7EiQ3nNrCDIzm9d6bLg1bvDbF29bwgueAcaBoBgxaxG6LhTggujg==
36 | -----END CERTIFICATE-----
37 | Bag Attributes
38 | localKeyID: 2A 08 48 DE A1 76 E7 4C 18 95 9D 65 06 B2 35 DB 19 F2 87 D5
39 | friendlyName: v54t03123..
40 | Key Attributes:
41 | -----BEGIN PRIVATE KEY-----
42 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHttzMJ2rxLwRE
43 | KFs8a1GSpvziHlYjAtJhKsEHqrN9VI+B4lHz2fKWyXfpXLdf3UnW3wVaW77rFQ3z
44 | UiF7m61hpBoGAeg0/isNLtE76gQK+rxMUoiYe3Vn7grFqvGsi0DnDx1C6okn+93L
45 | 9UU10cEWLZCms+JTBcAxC/Y2eSaG1ukA4KTz/OnyK1zZV6bTGJG4XAAenR6WVs1h
46 | SBoaiyscInj7MRYWHrgU3pw3uCAuwzWqGR2UzskerugofwP8wy6DZlVjcqOAhmg1
47 | soFBDR1+H9Q4iUnSWi9VA0LnWyrPUxPa8484EW+ojvhsz0115kAkaZ+7YnjHEcDr
48 | j0l1tbIxAgMBAAECggEAF/UPWYzPnwJf2vk9sJVtzKTW0x0xLLQASVhX8t5+95A2
49 | 7YLDsqKOH5koFvNgqqb2CPOUmyPvAW5Iz8vQvm43XJjTPQM/jWksAEHXJv50i7NL
50 | oUWJTe60GnU2D5jpCCS68rzKatDjBnaH7cPiEIm1RZQjtQury6p18AGaVdXZ/EoB
51 | 73rABBvEct33oLOmi734mc5P/WCQMCxL5hGU07a9f27Ga9aajn9/p10ni38pBu4d
52 | 6P+GgQF18Ry5MRpX6pIPpHAVtAOOcZgLodAJa+i4UqugDx9b9k3uegIK+CE03Sc1
53 | 7hxXlggkQNQoelkG0WksCG4ynl5PJwdgw7cMCoIWTQKBgQDlmvgnbzAo36Hyt615
54 | GD7neQ7joImFWS0VPKEQ1dQsL1j+QA4ALdictfS5q9sJw5dYvIWhvJfAT7REsScV
55 | 7pAKtVs8BjJB0hLq9h961JICPJgNpI98bjpc2KA9QLJ8ms0M3a536e0LtsazXqw3
56 | YYu3LWPZ/+SsksJJUpD/wuEOnwKBgQDerDuhICpPu3XfGRSsa4VIb2DDq6hfJ61h
57 | XI75uemdFsjVObsIoQ5+vzKwJmrkvwQ9K3xsclkWE1nHJm2z/5GevyvBV9OjecUV
58 | vu2sTUJXa3kp51LZRBBiMxTOax32Rvk4zOTdvkbKGITarUn2DkaJpp3ny/AnL3aE
59 | W/yzQsQdLwKBgFpWG5agV4ltW3F8tQL0+CLobWQ/0Hunt2YooZJXHxB3XINEPn3x
60 | i800heHbbOWtj8l4+vbElcwzT9DLBn7JiuC7s/as1W8yMQFC7uXL5tp8brLHcTDa
61 | yZmoHXucDd2aplyOh8tkPUCjnBAMiqOLfIYIlMW7uYPwhgKFOsl2KkgvAoGASQsC
62 | fHZKquD5Z3eYun6CuCIhAc67aotfaoKsO+rqYSdqNde8LLZ4RMd/Xx0bD3JBd7de
63 | F0zjQESUvKk0b0k0gXiAZ+nheMynA90fpdeqHXcZxc8l9DNJlIzhFF069OCPPDyq
64 | DrsuCXlFi14w1tAT/29yeOkDHDeLjxuFp2ANaO0CgYEAwjv3wnAFm8ohQGazhhNf
65 | h8WdnHMnJA1HviZYuPdYGTKwzrTViksUlT6Xh5aQmzQLSzBRP+c26vacoUDr65/P
66 | D9wmEw2qyveQ8JLJTqLNVtT6r4ySKSJ8qGND6yjc0SZOMcj97mDPxKTuuaRVVQAW
67 | j0li5Js7w4yn+6O7bksqEzI=
68 | -----END PRIVATE KEY-----
--------------------------------------------------------------------------------
/src/Builders/Sale/NoteBuilder.php:
--------------------------------------------------------------------------------
1 | setUblVersion($data['ublVersion'] ?? '2.1')
17 | ->setTipoDoc($data['tipoDoc'] ?? null)
18 | ->setSerie($data['serie'] ?? null)
19 | ->setCorrelativo($data['correlativo'] ?? null)
20 | ->setFechaEmision(
21 | isset($data['fechaEmision'])
22 | ? new \DateTime($data['fechaEmision'])
23 | : null
24 | )
25 | ->setCompany(
26 | (new CompanyBuilder())->build()
27 | )
28 | ->setClient(
29 | isset($data['client'])
30 | ? (new ClientBuilder())->build($data['client'])
31 | : null
32 | )
33 | ->setTipoMoneda($data['tipoMoneda'] ?? null)
34 | ->setSumOtrosCargos($data['sumOtrosCargos'] ?? null)
35 | ->setMtoOperGravadas($data['mtoOperGravadas'] ?? null)
36 | ->setMtoOperInafectas($data['mtoOperInafectas'] ?? null)
37 | ->setMtoOperExoneradas($data['mtoOperExoneradas'] ?? null)
38 | ->setMtoOperExportacion($data['mtoOperExportacion'] ?? null)
39 | ->setMtoOperGratuitas($data['mtoOperGratuitas'] ?? null)
40 | ->setMtoIGVGratuitas($data['mtoIGVGratuitas'] ?? null)
41 | ->setMtoIGV($data['mtoIGV'] ?? null)
42 | ->setMtoBaseIvap($data['mtoBaseIvap'] ?? null)
43 | ->setMtoIvap($data['mtoIvap'] ?? null)
44 | ->setMtoBaseIsc($data['mtoBaseIsc'] ?? null)
45 | ->setMtoIsc($data['mtoIsc'] ?? null)
46 | ->setMtoBaseOth($data['mtoBaseOth'] ?? null)
47 | ->setMtoOtrosTributos($data['mtoOtrosTributos'] ?? null)
48 | ->setIcbper($data['icbper'] ?? null)
49 | ->setTotalImpuestos($data['totalImpuestos'] ?? null)
50 | ->setRedondeo($data['redondeo'] ?? null)
51 | ->setMtoImpVenta($data['mtoImpVenta'] ?? null)
52 | ->setDetails(
53 | array_map(
54 | fn($detail) => (new SaleDetailBuilder())->build($detail),
55 | $data['details'] ?? []
56 | )
57 | )
58 | ->setLegends(
59 | array_map(
60 | fn($legend) => (new LegendBuilder())->build($legend),
61 | $data['legends'] ?? []
62 | )
63 | )
64 | ->setGuias(
65 | array_map(
66 | fn($guia) => (new DocumentBuilder())->build($guia),
67 | $data['guias'] ?? []
68 | )
69 | )
70 | ->setRelDocs(
71 | array_map(
72 | fn($relDoc) => (new DocumentBuilder())->build($relDoc),
73 | $data['relDocs'] ?? []
74 | )
75 | )
76 | ->setCompra($data['compra'] ?? null)
77 | ->setFormaPago(
78 | isset($data['formaPago'])
79 | ? (new PaymentTermsBuilder())->build($data['formaPago'])
80 | : null
81 | )
82 | ->setCuotas(
83 | array_map(
84 | fn($cuota) => (new CuotaBuilder())->build($cuota),
85 | $data['cuotas'] ?? []
86 | )
87 | )
88 |
89 | //Nota de venta
90 | ->setCodMotivo($data['codMotivo'] ?? null)
91 | ->setDesMotivo($data['desMotivo'] ?? null)
92 | ->setTipDocAfectado($data['tipDocAfectado'] ?? null)
93 | ->setNumDocfectado($data['numDocfectado'] ?? null)
94 | ->setPerception(
95 | isset($data['perception'])
96 | ? (new SalePerceptionBuilder())->build($data['perception'])
97 | : null
98 | )
99 | ->setValorVenta($data['valorVenta'] ?? null)
100 | ->setSubTotal($data['subTotal'] ?? null);
101 | }
102 | }
--------------------------------------------------------------------------------
/src/Services/SenderService.php:
--------------------------------------------------------------------------------
1 | $mode]);
25 |
26 | return $this;
27 | }
28 |
29 | public function setCompany(array $company): self
30 | {
31 | $defaultCompany = config('greenter.company');
32 | $customCompany = array_replace_recursive($defaultCompany, $company);
33 |
34 | config([
35 | 'greenter.company' => $customCompany
36 | ]);
37 |
38 | return $this;
39 | }
40 |
41 | public function getXmlSigned(string $type, array $data)
42 | {
43 | $document = (DocumentBuilderFactory::create($type))->build($data);
44 | $xml = (XmlBuilderFactory::create($type))->build($document);
45 |
46 | $certPath = config('greenter.company.certificate');
47 |
48 | if (!file_exists($certPath)) {
49 | throw new GreenterException("Certificate file not found: $certPath");
50 | }
51 |
52 | $signer = new SignedXml();
53 | $signer->setCertificate(file_get_contents($certPath));
54 | $xmlSigned = $signer->signXml($xml);
55 |
56 | return new XmlSigned($type, $document, $xmlSigned);
57 | }
58 |
59 | public function send(string $type, array $data): SunatResponse
60 | {
61 | try {
62 | $document = (DocumentBuilderFactory::create($type))->build($data);
63 |
64 | $sender = (SenderFactory::create($type))->build();
65 |
66 | $result = $sender->send($document);
67 | $result = $this->processResult($result, $sender);
68 |
69 | return new SunatResponse(
70 | $document,
71 | $result->getCdrZip(),
72 | $result->getCdrResponse(),
73 | $type === 'despatch'
74 | ? $sender->getLastXml()
75 | : $sender->getFactory()->getLastXml()
76 | );
77 | } catch (\Throwable $e) {
78 | throw new GreenterException(
79 | $e->getMessage(),
80 | (int)$e->getCode(),
81 | $e
82 | );
83 | }
84 | }
85 |
86 | public function sendXml(XmlSigned $xmlSigned)
87 | {
88 | try {
89 | $sender = (SenderFactory::create($xmlSigned->getType()))->build();
90 | $xml = $xmlSigned->getXml();
91 |
92 | $result = $xmlSigned->getType() === 'despatch'
93 | ? $sender->sendXml($xmlSigned->getDocument()->getName(), $xml)
94 | : $sender->sendXmlFile($xml);
95 |
96 | $result = $this->processResult($result, $sender);
97 |
98 | return new SunatResponse(
99 | $xmlSigned->getDocument(),
100 | $result->getCdrZip(),
101 | $result->getCdrResponse(),
102 | $xml
103 | );
104 |
105 | } catch (\Throwable $e) {
106 | throw new GreenterException(
107 | $e->getMessage(),
108 | (int)$e->getCode(),
109 | $e
110 | );
111 | }
112 | }
113 |
114 | private function processResult($result, $sender)
115 | {
116 | if (!$result->isSuccess()) {
117 | throw new GreenterException(
118 | $result->getError()->getMessage(),
119 | (int)$result->getError()->getCode()
120 | );
121 | }
122 |
123 | if ($result instanceof SummaryResult) {
124 | $ticket = $result->getTicket();
125 | $result = $sender->getStatus($ticket);
126 |
127 | if (!$result->isSuccess()) {
128 | throw new GreenterException(
129 | $result->getError()->getMessage(),
130 | (int)$result->getError()->getCode()
131 | );
132 | }
133 | }
134 |
135 | return $result;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/Builders/Sale/InvoiceBuilder.php:
--------------------------------------------------------------------------------
1 | setUblVersion($data['ublVersion'] ?? '2.1')
19 | ->setTipoDoc($data['tipoDoc'] ?? null)
20 | ->setSerie($data['serie'] ?? null)
21 | ->setCorrelativo($data['correlativo'] ?? null)
22 | ->setFechaEmision(
23 | isset($data['fechaEmision'])
24 | ? new \DateTime($data['fechaEmision'])
25 | : null
26 | )
27 | ->setCompany(
28 | (new CompanyBuilder())->build()
29 | )
30 | ->setClient(
31 | isset($data['client'])
32 | ? (new ClientBuilder())->build($data['client'])
33 | : null
34 | )
35 | ->setTipoMoneda($data['tipoMoneda'] ?? null)
36 | ->setSumOtrosCargos($data['sumOtrosCargos'] ?? null)
37 | ->setMtoOperGravadas($data['mtoOperGravadas'] ?? null)
38 | ->setMtoOperInafectas($data['mtoOperInafectas'] ?? null)
39 | ->setMtoOperExoneradas($data['mtoOperExoneradas'] ?? null)
40 | ->setMtoOperExportacion($data['mtoOperExportacion'] ?? null)
41 | ->setMtoOperGratuitas($data['mtoOperGratuitas'] ?? null)
42 | ->setMtoIGVGratuitas($data['mtoIGVGratuitas'] ?? null)
43 | ->setMtoIGV($data['mtoIGV'] ?? null)
44 | ->setMtoBaseIvap($data['mtoBaseIvap'] ?? null)
45 | ->setMtoIvap($data['mtoIvap'] ?? null)
46 | ->setMtoBaseIsc($data['mtoBaseIsc'] ?? null)
47 | ->setMtoIsc($data['mtoIsc'] ?? null)
48 | ->setMtoBaseOth($data['mtoBaseOth'] ?? null)
49 | ->setMtoOtrosTributos($data['mtoOtrosTributos'] ?? null)
50 | ->setIcbper($data['icbper'] ?? null)
51 |
52 | ->setTotalImpuestos($data['totalImpuestos'] ?? null)
53 | ->setRedondeo($data['redondeo'] ?? null)
54 | ->setMtoImpVenta($data['mtoImpVenta'] ?? null)
55 | ->setDetails(
56 | array_map(
57 | fn($detail) => (new SaleDetailBuilder())->build($detail),
58 | $data['details'] ?? []
59 | )
60 | )
61 | ->setLegends(
62 | array_map(
63 | fn($legend) => (new LegendBuilder())->build($legend),
64 | $data['legends'] ?? []
65 | )
66 | )
67 | ->setGuias(
68 | array_map(
69 | fn($guia) => (new DocumentBuilder())->build($guia),
70 | $data['guias'] ?? []
71 | )
72 | )
73 | ->setRelDocs(
74 | array_map(
75 | fn($relDoc) => (new DocumentBuilder())->build($relDoc),
76 | $data['relDocs'] ?? []
77 | )
78 | )
79 | ->setCompra($data['compra'] ?? null)
80 | ->setFormaPago(
81 | isset($data['formaPago'])
82 | ? (new PaymentTermsBuilder())->build($data['formaPago'])
83 | : null
84 | )
85 | ->setCuotas(
86 | array_map(
87 | fn($cuota) => (new CuotaBuilder())->build($cuota),
88 | $data['cuotas'] ?? []
89 | )
90 | )
91 | ->setTipoOperacion($data['tipoOperacion'] ?? null)
92 | ->setFecVencimiento(
93 | isset($data['fecVencimiento'])
94 | ? new \DateTime($data['fecVencimiento'])
95 | : null
96 | )
97 | ->setSumDsctoGlobal($data['sumDsctoGlobal'] ?? null)
98 | ->setMtoDescuentos($data['mtoDescuentos'] ?? null)
99 | ->setSumOtrosDescuentos($data['sumOtrosDescuentos'] ?? null)
100 | ->setDescuentos(
101 | array_map(
102 | fn($descuento) => (new ChargeBuilder())->build($descuento),
103 | $data['descuentos'] ?? []
104 | )
105 | )
106 | ->setCargos(
107 | array_map(
108 | fn($cargo) => (new ChargeBuilder())->build($cargo),
109 | $data['cargos'] ?? []
110 | )
111 | )
112 | ->setMtoCargos($data['mtoCargos'] ?? null)
113 | ->setTotalAnticipos($data['totalAnticipos'] ?? null)
114 | ->setPerception(
115 | isset($data['perception'])
116 | ? (new SalePerceptionBuilder())->build($data['perception'])
117 | : null
118 | )
119 | ->setGuiaEmbebida(
120 | isset($data['guiaEmbebida'])
121 | ? (new EmbededDespatchBuilder())->build($data['guiaEmbebida'])
122 | : null
123 | )
124 | ->setAnticipos(
125 | array_map(
126 | fn($anticipo) => (new PrepaymentBuilder())->build($anticipo),
127 | $data['anticipos'] ?? []
128 | )
129 | )
130 | ->setDetraccion(
131 | isset($data['detraccion'])
132 | ? (new DetractionBuilder())->build($data['detraccion'])
133 | : null
134 | )
135 | ->setSeller(
136 | isset($data['seller'])
137 | ? (new ClientBuilder())->build($data['seller'])
138 | : null
139 | )
140 | ->setValorVenta($data['valorVenta'] ?? null)
141 | ->setSubTotal($data['subTotal'] ?? null)
142 | ->setObservacion($data['observacion'] ?? null)
143 | ->setDireccionEntrega(
144 | isset($data['direccionEntrega'])
145 | ? (new AddressBuilder())->build($data['direccionEntrega'])
146 | : null
147 | );;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/stubs/templates/voided.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | {% set cp = doc.company %}
10 | {% set fecGen = doc.fecGeneracion|date('d/m/Y') %}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | |
19 | |
20 |
21 |
22 |
23 |
24 | {% if 'RA' in doc.name %}
25 | COMUNICACIÓN
26 |
27 | D E B A J A S
28 | {% else %}
29 | RESUMEN DIARIO DE
30 |
31 | REVERSIONES
32 | {% endif %}
33 | |
34 |
35 |
36 | |
37 |
38 | |
39 |
40 |
41 | |
42 | R.U.C.: {{ cp.ruc }}
43 | |
44 |
45 |
46 | |
47 | No.: {{ doc.correlativo }}
48 | |
49 |
50 |
51 |
52 | |
53 |
54 |
55 |
56 |
57 |
58 |
59 | |
60 | {{ cp.razonSocial }}
61 | |
62 |
63 |
64 | |
65 | Dirección: {{ cp.address.direccion }}
66 | |
67 |
68 |
69 | |
70 | {{ params.user.header|raw }}
71 | |
72 |
73 |
74 |
75 | |
76 |
77 |
78 |
79 |
80 |
81 | | Fecha de Comunicación: {{ doc.fecComunicacion|date('d/m/Y') }} |
82 | Fecha de Generación: {{ fecGen }} |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | | Fecha |
91 | Tipo de Documento |
92 | Nro. de Documento |
93 | Motivo |
94 |
95 | {% for det in doc.details %}
96 |
97 | | {{ fecGen }} |
98 | {{ det.tipoDoc|catalog('01') }} |
99 | {{ det.serie }}-{{ det.correlativo }} |
100 | {{ det.desMotivoBaja }} |
101 |
102 | {% endfor %}
103 |
104 |
105 |
106 | {% if max_items is defined and doc.details|length > max_items %}
107 |
108 | {% endif %}
109 | {% if params.system.hash is defined and params.system.hash %}
110 |
111 |
112 | Resumen: {{ params.system.hash }}
113 |
114 |
115 | {% endif %}
116 | |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/stubs/templates/summary.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 | {% set cp = doc.company %}
11 | {% set fecGen = doc.fecGeneracion|date('d/m/Y') %}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | |
20 | |
21 |
22 |
23 |
24 |
25 |
26 | RESUMEN DIARIO DE
27 |
28 | BOLETAS DE VENTA
29 | |
30 |
31 |
32 | |
33 |
34 | |
35 |
36 |
37 | |
38 | R.U.C.: {{ cp.ruc }}
39 | |
40 |
41 |
42 | |
43 | No.: {{ doc.correlativo }}
44 | |
45 |
46 |
47 |
48 | |
49 |
50 |
51 |
52 |
53 |
54 |
55 | |
56 | {{ cp.razonSocial }}
57 | |
58 |
59 |
60 | |
61 | Dirección: {{ cp.address.direccion }}
62 | |
63 |
64 |
65 | |
66 | {{ params.user.header|raw }}
67 | |
68 |
69 |
70 |
71 | |
72 |
73 |
74 |
75 |
76 |
77 | | Fecha de Emisión del Resumen: {{ doc.fecResumen|date('d/m/Y') }} |
78 | Fecha de Generación: {{ fecGen }} |
79 |
80 |
81 | | Moneda: {{ 'PEN'|catalog('021') }} |
82 | |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | | Documento |
91 | Condición |
92 | Impuestos |
93 | Totales |
94 | Imp. Total |
95 |
96 | {% for det in doc.details %}
97 |
98 | |
99 | {{ det.tipoDoc|catalog('01') }} {{ det.serieNro }}
100 | {% if det.docReferencia %}
101 | {% set ref = det.docReferencia %}
102 | DOC. REF. {{ ref.tipoDoc|catalog('01') }} {{ ref.nroDoc }}
103 | {% endif %}
104 | |
105 | {{ det.estado|catalog('19') }} |
106 |
107 | IGV {{ det.mtoIGV|n_format }}
108 | {% if det.mtoISC %}
109 | ISC {{ det.mtoISC|n_format }}
110 | {% endif %}
111 | {% if det.mtoIcbper %}
112 | ICBPER {{ det.mtoIcbper|n_format }}
113 | {% endif %}
114 | {% if det.mtoOtrosTributos %}
115 | Otros Tributos {{ det.mtoOtrosTributos|n_format }}
116 | {% endif %}
117 | {% if det.mtoOtrosCargos %}
118 | Otros Cargos {{ det.mtoOtrosCargos|n_format }}
119 | {% endif %}
120 | |
121 |
122 | {% if det.mtoOperGravadas %}
123 | Gravadas {{ det.mtoOperGravadas|n_format }}
124 | {% endif %}
125 | {% if det.mtoOperInafectas %}
126 | Inafectas {{ det.mtoOperInafectas|n_format }}
127 | {% endif %}
128 | {% if det.mtoOperExoneradas %}
129 | Exoneradas {{ det.mtoOperExoneradas|n_format }}
130 | {% endif %}
131 | {% if det.mtoOperGratuitas %}
132 | Gratuitas {{ det.mtoOperGratuitas|n_format }}
133 | {% endif %}
134 | |
135 | {{ det.total|n_format }}
136 | {% if det.percepcion and det.percepcion.mto %}
137 | {% set perc = det.percepcion %}
138 | Percepción {{ perc.mto|n_format }}
139 | Total Pagar {{ perc.mtoTotal|n_format }}
140 | {% endif %}
141 | |
142 |
143 | {% endfor %}
144 |
145 |
146 |
147 | {% if max_items is defined and doc.details|length > max_items %}
148 |
149 | {% endif %}
150 | {% if params.system.hash is defined and params.system.hash%}
151 |
152 |
153 | Resumen: {{ params.system.hash }}
154 |
155 |
156 | {% endif %}
157 | |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/stubs/templates/invoice2.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Invoice
6 |
162 |
163 |
164 |
176 |
177 |
178 |
179 |
EMITIDO A:
180 | {% set cl = doc.client %}
181 |
{{ cl.tipoDoc|catalog('06') }} {{ cl.numDoc }}
182 |
{{ cl.rznSocial }}
183 | {% if cl.address %}
{{ cl.address.direccion }}
{% endif %}
184 |
185 |
186 | {% set serieNro = doc.serie ~ '-' ~ doc.correlativo %}
187 |
{{ doc.tipoDoc|catalog('01') }} ELECTRÓNICA
188 |
{{ serieNro }}
189 |
EMITIDO: {{ doc.fechaEmision|date('d/m/Y') }}
190 |
191 |
192 |
193 |
194 |
195 | | CANT. |
196 | DESCRIPCION |
197 | UNIDAD |
198 | P. UNIT |
199 | TOTAL |
200 |
201 |
202 |
203 | {% set moneda = doc.tipoMoneda|catalog('02') %}
204 | {% for det in doc.details %}
205 |
206 | | {{ det.cantidad|n_format }} |
207 |
208 | {{ det.codProducto }}
209 | {{ det.descripcion }}
210 | |
211 | {{ det.unidad }} |
212 | {{ moneda }}{{ det.mtoValorUnitario }} |
213 | {{ moneda }}{{ det.mtoValorVenta }} |
214 |
215 | {% endfor %}
216 |
217 |
218 |
219 | |
220 | Op. Gravadas |
221 | {{ moneda }} {{ doc.mtoOperGravadas|n_format }} |
222 |
223 | {% if doc.mtoOperInafectas %}
224 |
225 | |
226 | Op. Inafectas |
227 | {{ moneda }} {{ doc.mtoOperInafectas|n_format }} |
228 |
229 | {% endif %}
230 | {% if doc.mtoOperExoneradas %}
231 |
232 | |
233 | Op. Exoneradas |
234 | {{ moneda }} {{ doc.mtoOperExoneradas|n_format }} |
235 |
236 | {% endif %}
237 | {% if doc.mtoOperGratuitas %}
238 |
239 | |
240 | Op. Gratuitas |
241 | {{ moneda }} {{ doc.mtoOperGratuitas|n_format }} |
242 |
243 | {% endif %}
244 |
245 | |
246 | IGV (18%) |
247 | {{ moneda }} {{ doc.mtoIGV|n_format }} |
248 |
249 | {% if doc.mtoISC %}
250 |
251 | |
252 | ISC |
253 | {{ moneda }} {{ doc.mtoISC|n_format }} |
254 |
255 | {% endif %}
256 | {% if doc.mtoOtrosTributos %}
257 |
258 | |
259 | Otros Tributos |
260 | {{ moneda }} {{ doc.mtoOtrosTributos|n_format }} |
261 |
262 | {% endif %}
263 |
264 | |
265 | TOTAL |
266 | {{ moneda }} {{ doc.mtoImpVenta|n_format }} |
267 |
268 |
269 |
270 |
271 |
272 |
273 | IMPORTE EN LETRAS {{ legend(doc.legends, '1000') }}
274 |
275 |
276 |
277 |
278 |
279 |
280 | {% if params.system.hash is defined and params.system.hash%}
281 | Resumen: {{ params.system.hash }}
282 | {% endif %}
283 | Representación Impresa de la {{ doc.tipoDoc|catalog('01') }} ELECTRÓNICA.
284 |
285 |
286 |
287 |
288 |
|image_b64('svg+xml') }})
289 |
290 |
291 |
292 |
293 |
--------------------------------------------------------------------------------
/stubs/templates/retention.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | {% set cp = doc.company %}
10 | {% set name = '20'|catalog('01') %}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | |
19 | |
20 |
21 |
22 |
23 |
24 |
25 | COMPROBANTE DE {{ name }}
26 |
27 | E L E C T R Ó N I C A
28 | |
29 |
30 |
31 | |
32 | R.U.C.: {{ cp.ruc }}
33 | |
34 |
35 |
36 | |
37 | {{ doc.serie }}-{{ doc.correlativo }}
38 | |
39 |
40 |
41 |
42 | |
43 |
44 |
45 |
46 |
47 |
48 |
49 | |
50 | {{ cp.razonSocial }}
51 | |
52 |
53 |
54 | |
55 | Dirección: {{ cp.address.direccion }}
56 | |
57 |
58 |
59 | |
60 | {{ params.user.header|raw }}
61 | |
62 |
63 |
64 |
65 | |
66 |
67 |
68 |
69 | {% set cl = doc.proveedor %}
70 |
71 |
72 | | Razón Social: {{ cl.rznSocial }} |
73 | {{ cl.tipoDoc|catalog('06') }}: {{ cl.numDoc }} |
74 |
75 |
76 | |
77 | Fecha Emisión: {{ doc.fechaEmision|date('d/m/Y') }}
78 | |
79 | Dirección: {% if cl.address %}{{ cl.address.direccion }}{% endif %} |
80 |
81 |
82 | | Régimen: {{ doc.regimen }} |
83 | Tasa: {{ doc.tasa|n_format }}% |
84 |
85 |
86 | | Tipo Moneda: {{ 'PEN'|catalog('021') }} |
87 | |
88 |
89 |
90 |
91 | {% set moneda = 'PEN'|catalog('02') %}
92 |
93 |
94 |
95 |
96 | | Comprobante |
97 | Retención |
98 |
99 |
100 | | Tipo |
101 | Numero |
102 | Fecha |
103 | Moneda |
104 | Total |
105 | Total Retenido |
106 | Total Pagado |
107 |
108 | {% for det in doc.details %}
109 |
110 | | {{ det.tipoDoc|catalog('01') }} |
111 | {{ det.numDoc }} |
112 | {{ det.fechaEmision|date('d/m/Y') }} |
113 | {{ det.moneda }} |
114 | {{ det.impTotal|n_format }} |
115 | {{ det.impRetenido|n_format }} |
116 | {{ det.impPagar|n_format }} |
117 |
118 | {% endfor %}
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | {% if doc.observacion %}
129 |
130 | Observaciones
131 |
132 | {{ doc.observacion }}
133 | {% endif %}
134 | |
135 |
136 |
137 |
138 | |
139 |
140 |
141 |
142 |
143 |
144 | | Total Retenido: |
145 | {{ moneda }} {{ doc.impRetenido|n_format }} |
146 |
147 |
148 | | Total Pagado: |
149 | {{ moneda }} {{ doc.impPagado|n_format }} |
150 |
151 |
152 |
153 | |
154 |
155 |
156 |
157 |
158 | {% if max_items is defined and doc.details|length > max_items %}
159 |
160 | {% endif %}
161 |
162 |
163 |
164 |
165 |
166 | {% if params.user.footer is defined %}
167 | {{ params.user.footer|raw }}
168 | {% endif %}
169 | {% if params.system.hash is defined and params.system.hash%}
170 | Resumen: {{ params.system.hash }}
171 | {% endif %}
172 | Representación Impresa de la {{ name }} ELECTRÓNICA.
173 |
174 | |
175 |
176 |
177 |
178 | |
179 |
180 |
181 |
--------------------------------------------------------------------------------
/stubs/templates/perception.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | {% set cp = doc.company %}
10 | {% set name = '40'|catalog('01') %}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | |
19 | |
20 |
21 |
22 |
23 |
24 |
25 | COMPROBANTE DE {{ name }}
26 |
27 | E L E C T R Ó N I C A
28 | |
29 |
30 |
31 | |
32 | R.U.C.: {{ cp.ruc }}
33 | |
34 |
35 |
36 | |
37 | {{ doc.serie }}-{{ doc.correlativo }}
38 | |
39 |
40 |
41 |
42 | |
43 |
44 |
45 |
46 |
47 |
48 |
49 | |
50 | {{ cp.razonSocial }}
51 | |
52 |
53 |
54 | |
55 | Dirección: {{ cp.address.direccion }}
56 | |
57 |
58 |
59 | |
60 | {{ params.user.header|raw }}
61 | |
62 |
63 |
64 |
65 | |
66 |
67 |
68 |
69 | {% set cl = doc.proveedor %}
70 |
71 |
72 | | Razón Social: {{ cl.rznSocial }} |
73 | {{ cl.tipoDoc|catalog('06') }}: {{ cl.numDoc }} |
74 |
75 |
76 | |
77 | Fecha Emisión: {{ doc.fechaEmision|date('d/m/Y') }}
78 | |
79 | Dirección: {% if cl.address %}{{ cl.address.direccion }}{% endif %} |
80 |
81 |
82 | | Régimen: {{ doc.regimen }} |
83 | Tasa: {{ doc.tasa|n_format }}% |
84 |
85 |
86 | | Tipo Moneda: {{ 'PEN'|catalog('021') }} |
87 | |
88 |
89 |
90 |
91 | {% set moneda = 'PEN'|catalog('02') %}
92 |
93 |
94 |
95 |
96 | | Comprobante |
97 | Percepción |
98 |
99 |
100 | | Tipo |
101 | Numero |
102 | Fecha |
103 | Moneda |
104 | Total |
105 | Total Percibido |
106 | Total Cobrado |
107 |
108 | {% for det in doc.details %}
109 |
110 | | {{ det.tipoDoc|catalog('01') }} |
111 | {{ det.numDoc }} |
112 | {{ det.fechaEmision|date('d/m/Y') }} |
113 | {{ det.moneda }} |
114 | {{ det.impTotal|n_format }} |
115 | {{ det.impPercibido|n_format }} |
116 | {{ det.impCobrar|n_format }} |
117 |
118 | {% endfor %}
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | {% if doc.observacion %}
129 |
130 | Observaciones
131 |
132 | {{ doc.observacion }}
133 | {% endif %}
134 | |
135 |
136 |
137 |
138 | |
139 |
140 |
141 |
142 |
143 |
144 | | Total Percibido: |
145 | {{ moneda }} {{ doc.impPercibido|n_format }} |
146 |
147 |
148 | | Total Cobrado: |
149 | {{ moneda }} {{ doc.impCobrado|n_format }} |
150 |
151 |
152 |
153 | |
154 |
155 |
156 |
157 |
158 | {% if max_items is defined and doc.details|length > max_items %}
159 |
160 | {% endif %}
161 |
162 |
163 |
164 |
165 |
166 | {% if params.user.footer is defined %}
167 | {{ params.user.footer|raw }}
168 | {% endif %}
169 | {% if params.system.hash is defined and params.system.hash%}
170 | Resumen: {{ params.system.hash }}
171 | {% endif %}
172 | Representación Impresa de la {{ name }} ELECTRÓNICA.
173 |
174 | |
175 |
176 |
177 |
178 | |
179 |
180 |
181 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Laravel Greenter
2 |
3 | **Laravel Greenter** es un paquete para emitir **comprobantes electrónicos** desde Laravel utilizando [Greenter](https://github.com/thegreenter/greenter). Permite:
4 |
5 | * Firmar comprobantes digitalmente
6 | * Enviarlos a SUNAT (SEE o API REST)
7 | * Generar su representación impresa en PDF (HTML y PDF)
8 |
9 | [](LICENSE)
10 |
11 | ## 📚 Tabla de Contenidos
12 |
13 | * [📦 Requisitos](#-requisitos)
14 | * [🚀 Instalación](#-instalación)
15 | * [⚙️ Configuración Inicial](#️-configuración-inicial)
16 | * [🏢 Datos de la Empresa Emisora](#-datos-de-la-empresa-emisora)
17 | * [🛠️ Cambiar a Producción](#️-cambiar-a-producción)
18 | * [🧰 Uso Básico](#-uso-básico)
19 | * [🧾 Emisión de Comprobante Electrónico](#-emisión-de-comprobante-electrónico)
20 | * [🔁 Emisión Dinámica para Múltiples Empresas](#-emisión-dinámica-para-múltiples-empresas)
21 | * [📦 Otros Tipos de Comprobantes](#-otros-tipos-de-comprobantes)
22 | * [🎨 Generar Representación Impresa](#-generar-representación-impresa)
23 | * [🧾 HTML](#-html)
24 | * [🖨️ PDF](#️-pdf)
25 | * [🎨 Personalizar Plantillas](#-personalizar-plantillas)
26 | * [🧪 Facades Disponibles](#-facades-disponibles)
27 | * [🧱 Estructura del Paquete](#-estructura-del-paquete)
28 | * [🔐 Seguridad Recomendada](#-seguridad-recomendada)
29 | * [📄 Licencia](#-licencia)
30 |
31 | ## 📦 Requisitos
32 |
33 | Este paquete requiere:
34 |
35 | * PHP >= 8.1
36 | * Laravel 11.x o superior
37 | * Extensiones PHP: `soap`, `openssl`
38 | * [wkhtmltopdf](https://wkhtmltopdf.org) (opcional, para generación de PDF)
39 |
40 | ## 🚀 Instalación
41 |
42 | Instala el paquete con Composer:
43 |
44 | ```bash
45 | composer require codersfree/laravel-greenter
46 | ```
47 |
48 | Publica los archivos de configuración y recursos:
49 |
50 | ```bash
51 | php artisan vendor:publish --tag=greenter-laravel
52 | ```
53 |
54 | Esto generará:
55 |
56 | * `config/greenter.php`: configuración principal del paquete
57 | * `public/images/logo.png`: logo usado en PDFs
58 | * `public/certs/certificate.pem`: certificado digital de prueba
59 |
60 | ## ⚙️ Configuración Inicial
61 |
62 | ### 🏢 Datos de la Empresa Emisora
63 |
64 | En `config/greenter.php`, configura los datos de la empresa emisora:
65 |
66 | ```php
67 | 'company' => [
68 | 'ruc' => '20000000001',
69 | 'razonSocial' => 'GREEN SAC',
70 | 'nombreComercial' => 'GREEN',
71 | 'address' => [
72 | 'ubigeo' => '150101',
73 | 'departamento' => 'LIMA',
74 | 'provincia' => 'LIMA',
75 | 'distrito' => 'LIMA',
76 | 'direccion' => 'Av. Villa Nueva 221',
77 | ],
78 | ]
79 | ```
80 |
81 | ### 🛠️ Cambiar a Producción
82 |
83 | Cuando estés listo para pasar a producción, edita el archivo `config/greenter.php`, cambia el valor de `mode` a `'prod'` y reemplaza las credenciales de prueba por las credenciales reales proporcionadas por SUNAT:
84 |
85 | ```php
86 | 'mode' => 'prod',
87 |
88 | 'company' => [
89 | 'certificate' => public_path('certs/certificate.pem'),
90 | 'clave_sol' => [
91 | 'user' => 'USUARIO_SOL',
92 | 'password' => 'CLAVE_SOL',
93 | ],
94 | 'credentials' => [
95 | 'client_id' => '...',
96 | 'client_secret' => '...',
97 | ],
98 | ],
99 | ```
100 |
101 | > ⚠️ **Importante:** Nunca subas tus certificados o credenciales a tu repositorio. Usa variables de entorno.
102 |
103 | ## 🧰 Uso Básico
104 |
105 | ### 🧾 Emisión de Comprobante Electrónico
106 |
107 | Primero define los datos del comprobante:
108 |
109 | ```php
110 | $data = [
111 | "ublVersion" => "2.1",
112 | "tipoOperacion" => "0101", // Catálogo 51
113 | "tipoDoc" => "01", // Catálogo 01
114 | "serie" => "F001",
115 | "correlativo" => "1",
116 | "fechaEmision" => now(),
117 | "formaPago" => [
118 | 'tipo' => 'Contado',
119 | ],
120 | "tipoMoneda" => "PEN", // Catálogo 02
121 | "client" => [
122 | "tipoDoc" => "6", // Catálogo 06
123 | "numDoc" => "20000000001",
124 | "rznSocial" => "EMPRESA X",
125 | ],
126 | "mtoOperGravadas" => 100.00,
127 | "mtoIGV" => 18.00,
128 | "totalImpuestos" => 18.00,
129 | "valorVenta" => 100.00,
130 | "subTotal" => 118.00,
131 | "mtoImpVenta" => 118.00,
132 | "details" => [
133 | [
134 | "codProducto" => "P001",
135 | "unidad" => "NIU", // Catálogo 03
136 | "cantidad" => 2,
137 | "mtoValorUnitario" => 50.00,
138 | "descripcion" => "PRODUCTO 1",
139 | "mtoBaseIgv" => 100,
140 | "porcentajeIgv" => 18.00,
141 | "igv" => 18.00,
142 | "tipAfeIgv" => "10",
143 | "totalImpuestos" => 18.00,
144 | "mtoValorVenta" => 100.00,
145 | "mtoPrecioUnitario" => 59.00,
146 | ],
147 | ],
148 | "legends" => [
149 | [
150 | "code" => "1000", // Catálogo 15
151 | "value" => "SON CIENTO DIECIOCHO CON 00/100 SOLES",
152 | ],
153 | ],
154 | ];
155 | ```
156 |
157 | Envía el comprobante a SUNAT:
158 |
159 | ```php
160 | use CodersFree\LaravelGreenter\Facades\Greenter;
161 | use Illuminate\Support\Facades\Storage;
162 |
163 | try {
164 | $response = Greenter::send('invoice', $data);
165 |
166 | $name = $response->getDocument()->getName();
167 | Storage::put("sunat/xml/{$name}.xml", $response->getXml());
168 | Storage::put("sunat/cdr/{$name}.zip", $response->getCdrZip());
169 |
170 | return response()->json([
171 | 'success' => true,
172 | 'cdrResponse' => $response->readCdr(),
173 | 'xml' => Storage::url("sunat/xml/{$name}.xml"),
174 | 'cdr' => Storage::url("sunat/cdr/{$name}.zip"),
175 | ]);
176 | } catch (\Throwable $e) {
177 | return response()->json([
178 | 'success' => false,
179 | 'code' => $e->getCode(),
180 | 'message' => $e->getMessage(),
181 | ], 500);
182 | }
183 | ```
184 |
185 | ### 🔁 Emisión Dinámica para Múltiples Empresas
186 |
187 | Puedes emitir comprobantes desde distintas empresas sin cambiar archivos de configuración:
188 |
189 | ```php
190 | $data = [ ... ]; // Datos del comprobante
191 |
192 | $response = Greenter::setCompany([
193 | 'ruc' => '20999999999',
194 | 'razonSocial' => 'Otra Empresa SAC',
195 | 'certificate' => Storage::path('certs/otro_cert.pem'),
196 | // Otros datos...
197 | ])->send('invoice', $data);
198 | ```
199 |
200 | ## 📦 Otros Tipos de Comprobantes
201 |
202 | Además de facturas, puedes emitir:
203 |
204 | | Tipo de Comprobante | Código | Descripción |
205 | |---------------------|--------------|---------------------------------|
206 | | Factura | `invoice` | Factura electrónica (01) |
207 | | Boleta | `invoice` | Boleta de venta (03) |
208 | | Nota de Crédito | `note` | Nota de crédito electrónica (07)|
209 | | Nota de Débito | `note` | Nota de débito electrónica (08) |
210 | | Guía de Remisión | `despatch` | Guía de remisión electrónica |
211 | | Resumen Diario | `summary` | Resumen diario de boletas (RC) |
212 | | Comunicación de Baja| `voided` | Comunicación de baja (RA) |
213 | | Retención | `retention` | Comprobante de retención |
214 | | Percepción | `perception` | Comprobante de percepción |
215 |
216 | Consulta la [documentación de Greenter](https://github.com/thegreenter/greenter) para ver los campos específicos de cada uno.
217 |
218 | ## 🎨 Generar Representación Impresa
219 |
220 | ### 🧾 HTML
221 |
222 | ```php
223 | $data = [ ... ];
224 | $response = Greenter::send('invoice', $data);
225 |
226 | $html = GreenterReport::generateHtml($response->getDocument());
227 | ```
228 |
229 | ### 🖨️ PDF
230 |
231 | Es necesario tener [wkhtmltopdf](https://wkhtmltopdf.org) instalado en el sistema para generar archivos PDF. Una vez instalado, configura la ruta del ejecutable en el archivo `config/greenter.php`:
232 |
233 | ```php
234 | 'report' => [
235 | 'bin_path' => '/usr/local/bin/wkhtmltopdf',
236 | ],
237 | ```
238 |
239 | ```php
240 | $data = [ ... ];
241 | $response = Greenter::send('invoice', $data);
242 |
243 | $pdf = GreenterReport::generatePdf($response->getDocument());
244 | Storage::put("sunat/pdf/{$name}.pdf", $pdf);
245 | ```
246 |
247 | ### ✏️ Modificar Parámetros y Opciones
248 |
249 | **Parámetros adicionales:**
250 |
251 | ```php
252 | $html = GreenterReport::setParams([
253 | 'system' => [
254 | 'logo' => public_path('images/logo.png'),
255 | 'hash' => '',
256 | ],
257 | 'user' => [
258 | 'header' => 'Telf: (01) 123456',
259 | 'extras' => [
260 | ['name' => 'CONDICIÓN DE PAGO', 'value' => 'Contado'],
261 | ['name' => 'VENDEDOR', 'value' => 'VENDEDOR PRINCIPAL'],
262 | ],
263 | 'footer' => 'Nro Resolución: 123456789
',
264 | ]
265 | ])->generateHtml($response->getDocument());
266 | ```
267 |
268 | **Opciones de generación:**
269 |
270 | ```php
271 | $html = GreenterReport::setOptions([
272 | 'no-outline',
273 | 'viewport-size' => '1280x1024',
274 | 'page-width' => '21cm',
275 | 'page-height' => '29.7cm',
276 | ])->generateHtml($response->getDocument());
277 | ```
278 |
279 | ### 🎨 Personalizar Plantillas
280 |
281 | Publica las plantillas del reporte:
282 |
283 | ```bash
284 | php artisan vendor:publish --tag=greenter-templates
285 | ```
286 |
287 | Ubicación por defecto:
288 | `resources/views/vendor/laravel-greenter`
289 |
290 | Puedes personalizar y cambiar la ruta:
291 |
292 | ```php
293 | 'report' => [
294 | 'template' => resource_path('templates/laravel-greenter'),
295 | ],
296 | ```
297 |
298 | ## 🧪 Facades Disponibles
299 |
300 | | Alias | Función principal |
301 | | ---------------- | ---------------------------------------------- |
302 | | `Greenter` | Firma y envía comprobantes electrónicos |
303 | | `GreenterReport` | Genera HTML o PDF de la representación impresa |
304 |
305 | ## 🧱 Estructura del Paquete
306 |
307 | Ejemplos de métodos disponibles:
308 |
309 | ```php
310 | Greenter::send('invoice', $data);
311 | GreenterReport::generateHtml($document);
312 | GreenterReport::generatePdf($document);
313 | ```
314 |
315 | ## 🔐 Seguridad Recomendada
316 |
317 | * Usa `.env` para tus claves y certificados
318 | * Nunca subas archivos sensibles al repositorio
319 | * Protege rutas usando `storage_path()` o `config_path()`
320 | * Valida los datos antes de emitir comprobantes
321 |
322 | ## 📄 Licencia
323 |
324 | Este paquete está bajo licencia MIT.
325 | Desarrollado por [CodersFree](https://codersfree.com)
--------------------------------------------------------------------------------
/stubs/templates/despatch.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | {% set cp = doc.company %}
10 | {% set name = doc.tipoDoc|catalog('01') %}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | |
19 | |
20 |
21 |
22 |
23 |
24 |
25 | {{ name }}
26 |
27 | E L E C T R Ó N I C A
28 | |
29 |
30 |
31 | |
32 | R.U.C.: {{ cp.ruc }}
33 | |
34 |
35 |
36 | |
37 | {{ doc.serie }}-{{ doc.correlativo }}
38 | |
39 |
40 |
41 |
42 | |
43 |
44 |
45 |
46 |
47 |
48 |
49 | |
50 | {{ cp.razonSocial }}
51 | |
52 |
53 |
54 | |
55 | Dirección: {{ cp.address.direccion }}
56 | |
57 |
58 |
59 | |
60 | {{ params.user.header|raw }}
61 | |
62 |
63 |
64 |
65 | |
66 |
67 |
68 |
69 |
70 | {% set cl = doc.destinatario %}
71 |
72 |
73 |
74 | | DESTINATARIO |
75 |
76 |
77 | | Razón Social: {{ cl.rznSocial }} |
78 | {{ cl.tipoDoc|catalog('06') }}: {{ cl.numDoc }} |
79 |
80 |
81 | | Dirección: {% if cl.address %}{{ cl.address.direccion }}{% endif %} |
82 |
83 |
84 |
85 |
86 | {% set cl = doc.destinatario %}
87 |
88 |
89 |
90 | | ENVIO |
91 |
92 |
93 | |
94 | Fecha Emisión: {{ doc.fechaEmision|date('d/m/Y') }}
95 | |
96 | Fecha Inicio de Traslado: {{ doc.envio.fecTraslado|date('d/m/Y') }} |
97 |
98 |
99 | | Motivo Traslado: {{ doc.envio.codTraslado|catalog('20') }} |
100 | Modalidad de Transporte: {{ doc.envio.modTraslado|catalog('18') }} |
101 |
102 |
103 | | Peso Bruto Total ({{ doc.envio.undPesoTotal }}): {{ doc.envio.pesoTotal }} |
104 | {% if doc.envio.numBultos %}Número de Bultos: {{ doc.envio.numBultos }}{% endif %} |
105 |
106 |
107 | | P. Partida: {{ doc.envio.partida.ubigueo }} - {{ doc.envio.partida.direccion }} |
108 | P. Llegada: {{ doc.envio.llegada.ubigueo }} - {{ doc.envio.llegada.direccion }} |
109 |
110 |
111 |
112 | {% set transportista = doc.envio.transportista %}
113 | {% set vehiculo = doc.envio.vehiculo %}
114 | {% set choferes = doc.envio.choferes %}
115 | {% set indicadores = doc.envio.indicadores %}
116 |
117 |
118 |
119 |
120 | | TRANSPORTE |
121 |
122 | {% if transportista %}
123 |
124 | | Razón Social: {{ transportista.rznSocial }} |
125 | {{ transportista.tipoDoc|catalog('06') }}: {{ transportista.numDoc }} |
126 |
127 | {% endif %}
128 | {% if vehiculo or choferes %}
129 |
130 |
131 | Vehículo principal: {{ vehiculo.placa }}
132 | {% for secundario in vehiculo.secundarios %}
133 |
134 | Vehículo secundario: {{ secundario.placa }}
135 | {% endfor %}
136 | |
137 |
138 | {% for chofer in choferes %}
139 | Conductor {{chofer.tipo}}: {{ chofer.tipoDoc|catalog('06') }} {{ chofer.nroDoc }} - {{ chofer.nombres }} {{ chofer.apellidos }}
140 | {% if not loop.last %}
141 |
142 | {% endif %}
143 | {% endfor %}
144 | |
145 |
146 | {% endif %}
147 |
148 |
149 |
150 |
151 |
152 |
153 | | Item |
154 | Código |
155 | Descripción |
156 | Unidad |
157 | Cantidad |
158 |
159 | {% for det in doc.details %}
160 |
161 | | {{ loop.index }} |
162 | {{ det.codigo }} |
163 | {{ det.descripcion }} |
164 | {{ det.unidad }} |
165 | {{ det.cantidad }} |
166 |
167 | {% endfor %}
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 | {% if doc.observacion %}
178 |
179 | Observaciones
180 |
181 | {{ doc.observacion }}
182 | {% endif %}
183 | |
184 |
185 |
186 |
187 | |
188 | |
189 |
190 |
191 | {% if max_items is defined and doc.details|length > max_items %}
192 |
193 | {% endif %}
194 |
195 |
196 |
197 |
198 |
199 |
200 | {% if params.user.footer is defined %}
201 | {{ params.user.footer|raw }}
202 | {% endif %}
203 | {% if params.system.hash is defined and params.system.hash%}
204 | Resumen: {{ params.system.hash }}
205 | {% endif %}
206 | Representación Impresa de la {{ name }} ELECTRÓNICA.
207 |
208 | |
209 |
210 |
211 | |
212 |
213 |
214 |
215 | |
216 |
217 |
218 |
219 |
--------------------------------------------------------------------------------
/stubs/templates/invoice.html.twig:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 | {% set cp = doc.company %}
10 | {% set isInvoice = doc.tipoDoc in ['01', '03'] %}
11 | {% set isNota = doc.tipoDoc in ['07', '08'] %}
12 | {% set isAnticipo = doc.totalAnticipos is defined and doc.totalAnticipos > 0 %}
13 | {% set name = doc.tipoDoc|catalog('01') %}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | |
22 | |
23 |
24 |
25 |
26 |
27 |
28 | {{ name }}
29 |
30 | E L E C T R Ó N I C A
31 | |
32 |
33 |
34 | |
35 | R.U.C.: {{ cp.ruc }}
36 | |
37 |
38 |
39 | |
40 | {{ doc.serie }}-{{ doc.correlativo }}
41 | |
42 |
43 |
44 |
45 | |
46 |
47 |
48 |
49 |
50 |
51 |
52 | |
53 | {{ cp.razonSocial }}
54 | |
55 |
56 |
57 | |
58 | Dirección: {{ cp.address.direccion }}
59 | |
60 |
61 |
62 | |
63 | {{ params.user.header|raw }}
64 | |
65 |
66 |
67 |
68 | |
69 |
70 |
71 |
72 | {% set cl = doc.client %}
73 |
74 |
75 | | Razón Social: {{ cl.rznSocial }} |
76 | {{ cl.tipoDoc|catalog('06') }}: {{ cl.numDoc }} |
77 |
78 |
79 |
80 | Fecha Emisión: {{ doc.fechaEmision|date('d/m/Y') }}
81 | {% if doc.fechaEmision|date('H:i:s') != '00:00:00' %} {{ doc.fechaEmision|date('H:i:s') }} {% endif %}
82 | {% if doc.fecVencimiento is defined and doc.fecVencimiento %}
83 |
Fecha Vencimiento: {{ doc.fecVencimiento|date('d/m/Y') }}
84 | {% endif %}
85 | |
86 | Dirección: {% if cl.address %}{{ cl.address.direccion }}{% endif %} |
87 |
88 | {% if isNota %}
89 |
90 | | Tipo Doc. Ref.: {{ doc.tipDocAfectado|catalog('01') }} |
91 | Documento Ref.: {{ doc.numDocfectado }} |
92 |
93 | {% endif %}
94 |
95 | | Tipo Moneda: {{ doc.tipoMoneda|catalog('021') }} |
96 | {% if doc.compra is defined and doc.compra %}O/C: {{ doc.compra }}{% endif %} |
97 |
98 | {% if doc.guias %}
99 |
100 | | Guias:
101 | {% for guia in doc.guias %}
102 | {{ guia.nroDoc }}
103 | {% endfor %} |
104 | |
105 |
106 | {% endif %}
107 |
108 |
109 | {% set moneda = doc.tipoMoneda|catalog('02') %}
110 |
111 |
112 |
113 |
114 | | Cantidad |
115 | Código |
116 | Descripción |
117 | Valor Unitario |
118 | Valor Total |
119 |
120 | {% for det in doc.details %}
121 |
122 | |
123 | {{ det.cantidad|n_format }}
124 | {{ det.unidad }}
125 | |
126 |
127 | {{ det.codProducto }}
128 | |
129 |
130 | {{ det.descripcion }}
131 | |
132 |
133 | {{ moneda }}
134 | {{ det.mtoValorUnitario|n_format }}
135 | |
136 |
137 | {{ moneda }}
138 | {{ det.mtoValorVenta|n_format }}
139 | |
140 |
141 | {% endfor %}
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | {{ legend(doc.legends, '1000') }}
154 |
155 |
156 | Información Adicional
157 | |
158 |
159 |
160 |
161 |
162 |
163 |
164 | |
165 | LEYENDA:
166 | |
167 |
168 |
169 | {% for leg in doc.legends %}
170 | {% if leg.code != '1000' %}
171 | {{ leg.value }}
172 | {% endif %}
173 | {% endfor %}
174 |
175 | |
176 |
177 | {% if isInvoice and doc.detraccion %}
178 |
179 | |
180 | TIPO DE DETRACCIÓN:
181 | |
182 |
183 | {{ doc.detraccion.codBienDetraccion }} - {{ doc.detraccion.codBienDetraccion|catalog('54') }}
184 | |
185 |
186 |
187 | |
188 | MEDIO DE PAGO:
189 | |
190 |
191 | {{ doc.detraccion.codMedioPago }} - {{ doc.detraccion.codMedioPago|catalog('59') }}
192 | |
193 |
194 |
195 | |
196 | N° CTA. BANCO NAC:
197 | |
198 |
199 | {{ doc.detraccion.ctaBanco }}
200 | |
201 |
202 | {% endif %}
203 | {% if isNota %}
204 |
205 | |
206 | MOTIVO DE EMISIÓN:
207 | |
208 |
209 | {{ doc.desMotivo }}
210 | |
211 |
212 | {% endif %}
213 | {% if params.user.extras is defined %}
214 | {% for item in params.user.extras %}
215 |
216 | |
217 | {{ item.name|upper }}:
218 | |
219 |
220 | {{ item.value }}
221 | |
222 |
223 | {% endfor %}
224 | {% endif %}
225 |
226 |
227 | {% if isAnticipo %}
228 |
229 |
230 |
231 |
232 |
233 | Anticipo
234 |
235 | |
236 |
237 |
238 |
239 |
240 |
241 |
242 | | Nro. Doc. |
243 | Total |
244 |
245 | {% for atp in doc.anticipos %}
246 |
247 | | {{ atp.nroDocRel }} |
248 | {{ moneda }} {{ atp.total|n_format }} |
249 |
250 | {% endfor %}
251 |
252 |
253 | {% endif %}
254 | {% if doc.cuotas %}
255 |
256 |
257 |
258 |
259 |
260 | Cuotas
261 |
262 | |
263 |
264 |
265 |
266 |
267 |
268 |
269 | | Monto |
270 | Fecha Pago |
271 |
272 | {% for cuota in doc.cuotas %}
273 |
274 | | {{ moneda }} {{ cuota.monto|n_format }} |
275 | {{ cuota.fechaPago|date('d/m/Y') }} |
276 |
277 | {% endfor %}
278 |
279 |
280 | {% endif %}
281 | |
282 |
283 |
284 |
285 |
286 | {% if isAnticipo %}
287 |
288 | | Total Anticipo: |
289 | {{ moneda }} {{ doc.totalAnticipos|n_format }} |
290 |
291 | {% endif %}
292 | {% if doc.mtoOperGravadas %}
293 |
294 | | Op. Gravadas: |
295 | {{ moneda }} {{ doc.mtoOperGravadas|n_format }} |
296 |
297 | {% endif %}
298 | {% if doc.mtoOperInafectas %}
299 |
300 | | Op. Inafectas: |
301 | {{ moneda }} {{ doc.mtoOperInafectas|n_format }} |
302 |
303 | {% endif %}
304 | {% if doc.mtoOperExoneradas %}
305 |
306 | | Op. Exoneradas: |
307 | {{ moneda }} {{ doc.mtoOperExoneradas|n_format }} |
308 |
309 | {% endif %}
310 | {% if doc.mtoOperGratuitas %}
311 |
312 | | Op. Gratuitas: |
313 | {{ moneda }} {{ doc.mtoOperGratuitas|n_format }} |
314 |
315 | {% endif %}
316 | {% if doc.mtoOperExportacion %}
317 |
318 | | Op. Exportación: |
319 | {{ moneda }} {{ doc.mtoOperExportacion|n_format }} |
320 |
321 | {% endif %}
322 | {% if doc.mtoBaseIvap %}
323 |
324 | | Base IVAP: |
325 | {{ moneda }} {{ doc.mtoBaseIvap|n_format }} |
326 |
327 | {% endif %}
328 | {% if doc.mtoIGVGratuitas %}
329 |
330 | | IGV Gratuito: |
331 | {{ moneda }} {{ doc.mtoIGVGratuitas|n_format }} |
332 |
333 | {% endif %}
334 |
335 | | I.G.V.{% if params.user.numIGV is defined %} {{ params.user.numIGV }}%{% endif %}: |
336 | {{ moneda }} {{ doc.mtoIGV|n_format }} |
337 |
338 | {% if doc.mtoIvap %}
339 |
340 | | IVAP: |
341 | {{ moneda }} {{ doc.mtoIvap|n_format }} |
342 |
343 | {% endif %}
344 | {% if doc.icbper %}
345 |
346 | | I.C.B.P.E.R.: |
347 | {{ moneda }} {{ doc.icbper|n_format }} |
348 |
349 | {% endif %}
350 | {% if doc.mtoISC %}
351 |
352 | | I.S.C.: |
353 | {{ moneda }} {{ doc.mtoISC|n_format }} |
354 |
355 | {% endif %}
356 | {% if doc.sumOtrosCargos %}
357 |
358 | | Otros Cargos: |
359 | {{ moneda }} {{ doc.sumOtrosCargos|n_format }} |
360 |
361 | {% endif %}
362 | {% if doc.mtoOtrosTributos %}
363 |
364 | | Otros Tributos: |
365 | {{ moneda }} {{ doc.mtoOtrosTributos|n_format }} |
366 |
367 | {% endif %}
368 | {% if doc.redondeo %}
369 |
370 | | Redondeo: |
371 | {{ moneda }} {{ doc.redondeo|n_format }} |
372 |
373 | {% endif %}
374 |
375 | | Precio Venta: |
376 | {{ moneda }} {{ doc.mtoImpVenta|n_format }} |
377 |
378 | {% if doc.perception and doc.perception.mto %}
379 | {% set perc = doc.perception %}
380 | {% set soles = 'PEN'|catalog('02') %}
381 |
382 | | Percepción: |
383 | {{ soles }} {{ perc.mto|n_format }} |
384 |
385 |
386 | | Total a Pagar: |
387 | {{ soles }} {{ perc.mtoTotal|n_format }} |
388 |
389 | {% endif %}
390 | {% if isInvoice and doc.detraccion %}
391 |
392 | | Detracción ({{doc.detraccion.percent}}%): |
393 | {{ moneda }} {{ doc.detraccion.mount|n_format }} |
394 |
395 | {% endif %}
396 |
397 |
398 | |
399 |
400 |
401 |
402 |
403 | {% if max_items is defined and doc.details|length > max_items %}
404 |
405 | {% endif %}
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 | {% if params.user.footer is defined %}
414 | {{ params.user.footer|raw }}
415 | {% endif %}
416 | {% if params.system.hash is defined and params.system.hash%}
417 | Resumen: {{ params.system.hash }}
418 | {% endif %}
419 | Representación Impresa de la {{ name }} ELECTRÓNICA.
420 |
421 | |
422 |
423 |
424 | |
425 |
426 |
427 |
428 |
429 | |
430 |
431 |
432 |
433 |
--------------------------------------------------------------------------------