├── .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 | 117 | 118 |
14 | 15 | 16 | 19 | 20 | 53 | 54 | 55 | 76 | 77 |
17 | 18 | 21 |
22 | 23 | 34 | 35 | 36 | 39 | 40 | 41 | 44 | 45 | 46 | 49 | 50 |
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 |
37 |   38 |
42 | R.U.C.: {{ cp.ruc }} 43 |
47 | No.: {{ doc.correlativo }} 48 |
51 |
52 |
56 |
57 | 58 | 59 | 62 | 63 | 64 | 67 | 68 | 69 | 72 | 73 |
60 | {{ cp.razonSocial }} 61 |
65 | Dirección: {{ cp.address.direccion }} 66 |
70 | {{ params.user.header|raw }} 71 |
74 |
75 |
78 |
79 | 80 | 81 | 82 | 83 | 84 |
Fecha de Comunicación: {{ doc.fecComunicacion|date('d/m/Y') }}Fecha de Generación: {{ fecGen }}
85 |

86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | {% for det in doc.details %} 96 | 97 | 98 | 99 | 100 | 101 | 102 | {% endfor %} 103 | 104 |
FechaTipo de DocumentoNro. de DocumentoMotivo
{{ fecGen }}{{ det.tipoDoc|catalog('01') }}{{ det.serie }}-{{ det.correlativo }}{{ det.desMotivoBaja }}
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 |
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 | 158 | 159 |
15 | 16 | 17 | 20 | 21 | 49 | 50 | 51 | 72 | 73 |
18 | 19 | 22 |
23 | 24 | 25 | 30 | 31 | 32 | 35 | 36 | 37 | 40 | 41 | 42 | 45 | 46 |
26 | RESUMEN DIARIO DE 27 |
28 | BOLETAS DE VENTA 29 |
33 |   34 |
38 | R.U.C.: {{ cp.ruc }} 39 |
43 | No.: {{ doc.correlativo }} 44 |
47 |
48 |
52 |
53 | 54 | 55 | 58 | 59 | 60 | 63 | 64 | 65 | 68 | 69 |
56 | {{ cp.razonSocial }} 57 |
61 | Dirección: {{ cp.address.direccion }} 62 |
66 | {{ params.user.header|raw }} 67 |
70 |
71 |
74 |
75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
Fecha de Emisión del Resumen: {{ doc.fecResumen|date('d/m/Y') }}Fecha de Generación: {{ fecGen }}
Moneda: {{ 'PEN'|catalog('021') }}
85 |

86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | {% for det in doc.details %} 97 | 98 | 105 | 106 | 121 | 135 | 142 | 143 | {% endfor %} 144 | 145 |
DocumentoCondiciónImpuestosTotalesImp. Total
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 |
{{ det.estado|catalog('19') }} 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 |
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 |
{{ 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 |
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 |
160 | -------------------------------------------------------------------------------- /stubs/templates/invoice2.html.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Invoice 6 | 162 | 163 | 164 |
165 | 168 |
169 | {% set cp = doc.company %} 170 |

RUC {{ cp.ruc }}

171 |

{{ cp.razonSocial }}

172 |
{{ cp.address.direccion }}
173 |
{{ params.user.header|raw }}
174 |
175 |
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 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | {% set moneda = doc.tipoMoneda|catalog('02') %} 204 | {% for det in doc.details %} 205 | 206 | 207 | 211 | 212 | 213 | 214 | 215 | {% endfor %} 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | {% if doc.mtoOperInafectas %} 224 | 225 | 226 | 227 | 228 | 229 | {% endif %} 230 | {% if doc.mtoOperExoneradas %} 231 | 232 | 233 | 234 | 235 | 236 | {% endif %} 237 | {% if doc.mtoOperGratuitas %} 238 | 239 | 240 | 241 | 242 | 243 | {% endif %} 244 | 245 | 246 | 247 | 248 | 249 | {% if doc.mtoISC %} 250 | 251 | 252 | 253 | 254 | 255 | {% endif %} 256 | {% if doc.mtoOtrosTributos %} 257 | 258 | 259 | 260 | 261 | 262 | {% endif %} 263 | 264 | 265 | 266 | 267 | 268 | 269 |
CANT.DESCRIPCIONUNIDADP. UNITTOTAL
{{ det.cantidad|n_format }} 208 |

{{ det.codProducto }}

209 | {{ det.descripcion }} 210 |
{{ det.unidad }}{{ moneda }}{{ det.mtoValorUnitario }}{{ moneda }}{{ det.mtoValorVenta }}
Op. Gravadas{{ moneda }} {{ doc.mtoOperGravadas|n_format }}
Op. Inafectas{{ moneda }} {{ doc.mtoOperInafectas|n_format }}
Op. Exoneradas{{ moneda }} {{ doc.mtoOperExoneradas|n_format }}
Op. Gratuitas{{ moneda }} {{ doc.mtoOperGratuitas|n_format }}
IGV (18%){{ moneda }} {{ doc.mtoIGV|n_format }}
ISC{{ moneda }} {{ doc.mtoISC|n_format }}
Otros Tributos{{ moneda }} {{ doc.mtoOtrosTributos|n_format }}
TOTAL{{ moneda }} {{ doc.mtoImpVenta|n_format }}
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 | 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 | 179 | 180 |
14 | 15 | 16 | 19 | 20 | 43 | 44 | 45 | 66 | 67 |
17 | 18 | 21 |
22 | 23 | 24 | 29 | 30 | 31 | 34 | 35 | 36 | 39 | 40 |
25 | COMPROBANTE DE {{ name }} 26 |
27 | E L E C T R Ó N I C A 28 |
32 | R.U.C.: {{ cp.ruc }} 33 |
37 | {{ doc.serie }}-{{ doc.correlativo }} 38 |
41 |
42 |
46 |
47 | 48 | 49 | 52 | 53 | 54 | 57 | 58 | 59 | 62 | 63 |
50 | {{ cp.razonSocial }} 51 |
55 | Dirección: {{ cp.address.direccion }} 56 |
60 | {{ params.user.header|raw }} 61 |
64 |
65 |
68 |
69 | {% set cl = doc.proveedor %} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 |
Razón Social: {{ cl.rznSocial }}{{ cl.tipoDoc|catalog('06') }}: {{ cl.numDoc }}
77 | Fecha Emisión: {{ doc.fechaEmision|date('d/m/Y') }} 78 | Dirección: {% if cl.address %}{{ cl.address.direccion }}{% endif %}
Régimen: {{ doc.regimen }} Tasa: {{ doc.tasa|n_format }}%
Tipo Moneda: {{ 'PEN'|catalog('021') }}
90 |

91 | {% set moneda = 'PEN'|catalog('02') %} 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | {% for det in doc.details %} 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | {% endfor %} 119 | 120 |
ComprobanteRetención
TipoNumeroFechaMonedaTotalTotal RetenidoTotal Pagado
{{ det.tipoDoc|catalog('01') }}{{ det.numDoc }}{{ det.fechaEmision|date('d/m/Y') }}{{ det.moneda }}{{ det.impTotal|n_format }}{{ det.impRetenido|n_format }}{{ det.impPagar|n_format }}
121 | 122 | 123 | 139 | 154 | 155 |
124 | 125 | 126 | 127 | 135 | 136 | 137 |
128 | {% if doc.observacion %} 129 |

130 | Observaciones 131 |
132 |

{{ doc.observacion }}

133 | {% endif %} 134 |
138 |
140 |
141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 |
Total Retenido:{{ moneda }} {{ doc.impRetenido|n_format }}
Total Pagado:{{ moneda }} {{ doc.impPagado|n_format }}
153 |
156 |
157 |
158 | {% if max_items is defined and doc.details|length > max_items %} 159 |
160 | {% endif %} 161 |
162 | 163 | 164 | 175 | 176 |
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 |
177 |
178 |
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 | 179 | 180 |
14 | 15 | 16 | 19 | 20 | 43 | 44 | 45 | 66 | 67 |
17 | 18 | 21 |
22 | 23 | 24 | 29 | 30 | 31 | 34 | 35 | 36 | 39 | 40 |
25 | COMPROBANTE DE {{ name }} 26 |
27 | E L E C T R Ó N I C A 28 |
32 | R.U.C.: {{ cp.ruc }} 33 |
37 | {{ doc.serie }}-{{ doc.correlativo }} 38 |
41 |
42 |
46 |
47 | 48 | 49 | 52 | 53 | 54 | 57 | 58 | 59 | 62 | 63 |
50 | {{ cp.razonSocial }} 51 |
55 | Dirección: {{ cp.address.direccion }} 56 |
60 | {{ params.user.header|raw }} 61 |
64 |
65 |
68 |
69 | {% set cl = doc.proveedor %} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 |
Razón Social: {{ cl.rznSocial }}{{ cl.tipoDoc|catalog('06') }}: {{ cl.numDoc }}
77 | Fecha Emisión: {{ doc.fechaEmision|date('d/m/Y') }} 78 | Dirección: {% if cl.address %}{{ cl.address.direccion }}{% endif %}
Régimen: {{ doc.regimen }} Tasa: {{ doc.tasa|n_format }}%
Tipo Moneda: {{ 'PEN'|catalog('021') }}
90 |

91 | {% set moneda = 'PEN'|catalog('02') %} 92 |
93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | {% for det in doc.details %} 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | {% endfor %} 119 | 120 |
ComprobantePercepción
TipoNumeroFechaMonedaTotalTotal PercibidoTotal Cobrado
{{ det.tipoDoc|catalog('01') }}{{ det.numDoc }}{{ det.fechaEmision|date('d/m/Y') }}{{ det.moneda }}{{ det.impTotal|n_format }}{{ det.impPercibido|n_format }}{{ det.impCobrar|n_format }}
121 | 122 | 123 | 139 | 154 | 155 |
124 | 125 | 126 | 127 | 135 | 136 | 137 |
128 | {% if doc.observacion %} 129 |

130 | Observaciones 131 |
132 |

{{ doc.observacion }}

133 | {% endif %} 134 |
138 |
140 |
141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 |
Total Percibido:{{ moneda }} {{ doc.impPercibido|n_format }}
Total Cobrado:{{ moneda }} {{ doc.impCobrado|n_format }}
153 |
156 |
157 |
158 | {% if max_items is defined and doc.details|length > max_items %} 159 |
160 | {% endif %} 161 |
162 | 163 | 164 | 175 | 176 |
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 |
177 |
178 |
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 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](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 | 216 | 217 |
14 | 15 | 16 | 19 | 20 | 43 | 44 | 45 | 66 | 67 |
17 | 18 | 21 |
22 | 23 | 24 | 29 | 30 | 31 | 34 | 35 | 36 | 39 | 40 |
25 | {{ name }} 26 |
27 | E L E C T R Ó N I C A 28 |
32 | R.U.C.: {{ cp.ruc }} 33 |
37 | {{ doc.serie }}-{{ doc.correlativo }} 38 |
41 |
42 |
46 |
47 | 48 | 49 | 52 | 53 | 54 | 57 | 58 | 59 | 62 | 63 |
50 | {{ cp.razonSocial }} 51 |
55 | Dirección: {{ cp.address.direccion }} 56 |
60 | {{ params.user.header|raw }} 61 |
64 |
65 |
68 |
69 |
70 | {% set cl = doc.destinatario %} 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
DESTINATARIO
Razón Social: {{ cl.rznSocial }}{{ cl.tipoDoc|catalog('06') }}: {{ cl.numDoc }}
Dirección: {% if cl.address %}{{ cl.address.direccion }}{% endif %}
84 |

85 |
86 | {% set cl = doc.destinatario %} 87 | 88 | 89 | 90 | 91 | 92 | 93 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
ENVIO
94 | Fecha Emisión: {{ doc.fechaEmision|date('d/m/Y') }} 95 | Fecha Inicio de Traslado: {{ doc.envio.fecTraslado|date('d/m/Y') }}
Motivo Traslado: {{ doc.envio.codTraslado|catalog('20') }} Modalidad de Transporte: {{ doc.envio.modTraslado|catalog('18') }}
Peso Bruto Total ({{ doc.envio.undPesoTotal }}): {{ doc.envio.pesoTotal }} {% if doc.envio.numBultos %}Número de Bultos: {{ doc.envio.numBultos }}{% endif %}
P. Partida: {{ doc.envio.partida.ubigueo }} - {{ doc.envio.partida.direccion }}P. Llegada: {{ doc.envio.llegada.ubigueo }} - {{ doc.envio.llegada.direccion }}
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 | 121 | 122 | {% if transportista %} 123 | 124 | 125 | 126 | 127 | {% endif %} 128 | {% if vehiculo or choferes %} 129 | 130 | 137 | 145 | 146 | {% endif %} 147 |
TRANSPORTE
Razón Social: {{ transportista.rznSocial }}{{ transportista.tipoDoc|catalog('06') }}: {{ transportista.numDoc }}
131 | Vehículo principal: {{ vehiculo.placa }} 132 | {% for secundario in vehiculo.secundarios %} 133 |
134 | Vehículo secundario: {{ secundario.placa }} 135 | {% endfor %} 136 |
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 |
148 |

149 |
150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | {% for det in doc.details %} 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | {% endfor %} 168 | 169 |
ItemCódigoDescripciónUnidadCantidad
{{ loop.index }}{{ det.codigo }}{{ det.descripcion }}{{ det.unidad }}{{ det.cantidad }}
170 | 171 | 172 | 188 | 189 | 190 |
173 | 174 | 175 | 176 | 184 | 185 | 186 |
177 | {% if doc.observacion %} 178 |

179 | Observaciones 180 |
181 |

{{ doc.observacion }}

182 | {% endif %} 183 |
187 |
191 | {% if max_items is defined and doc.details|length > max_items %} 192 |
193 | {% endif %} 194 |
195 | 196 | 197 | 198 | 209 | 212 | 213 |
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 |
210 | Qr Image 211 |
214 |
215 |
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 | 430 | 431 |
17 | 18 | 19 | 22 | 23 | 46 | 47 | 48 | 69 | 70 |
20 | 21 | 24 |
25 | 26 | 27 | 32 | 33 | 34 | 37 | 38 | 39 | 42 | 43 |
28 | {{ name }} 29 |
30 | E L E C T R Ó N I C A 31 |
35 | R.U.C.: {{ cp.ruc }} 36 |
40 | {{ doc.serie }}-{{ doc.correlativo }} 41 |
44 |
45 |
49 |
50 | 51 | 52 | 55 | 56 | 57 | 60 | 61 | 62 | 65 | 66 |
53 | {{ cp.razonSocial }} 54 |
58 | Dirección: {{ cp.address.direccion }} 59 |
63 | {{ params.user.header|raw }} 64 |
67 |
68 |
71 |
72 | {% set cl = doc.client %} 73 | 74 | 75 | 76 | 77 | 78 | 79 | 86 | 87 | 88 | {% if isNota %} 89 | 90 | 91 | 92 | 93 | {% endif %} 94 | 95 | 96 | 97 | 98 | {% if doc.guias %} 99 | 100 | 104 | 105 | 106 | {% endif %} 107 |
Razón Social: {{ cl.rznSocial }}{{ cl.tipoDoc|catalog('06') }}: {{ cl.numDoc }}
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 |
Dirección: {% if cl.address %}{{ cl.address.direccion }}{% endif %}
Tipo Doc. Ref.: {{ doc.tipDocAfectado|catalog('01') }}Documento Ref.: {{ doc.numDocfectado }}
Tipo Moneda: {{ doc.tipoMoneda|catalog('021') }} {% if doc.compra is defined and doc.compra %}O/C: {{ doc.compra }}{% endif %}
Guias: 101 | {% for guia in doc.guias %} 102 | {{ guia.nroDoc }}   103 | {% endfor %}
108 |

109 | {% set moneda = doc.tipoMoneda|catalog('02') %} 110 |
111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | {% for det in doc.details %} 121 | 122 | 126 | 129 | 132 | 136 | 140 | 141 | {% endfor %} 142 | 143 |
CantidadCódigoDescripciónValor UnitarioValor Total
123 | {{ det.cantidad|n_format }} 124 | {{ det.unidad }} 125 | 127 | {{ det.codProducto }} 128 | 130 | {{ det.descripcion }}
131 |
133 | {{ moneda }} 134 | {{ det.mtoValorUnitario|n_format }} 135 | 137 | {{ moneda }} 138 | {{ det.mtoValorVenta|n_format }} 139 |
144 | 145 | 146 | 282 | 399 | 400 |
147 | 148 | 149 | 150 | 158 | 159 | 160 |
151 |
152 |
153 | {{ legend(doc.legends, '1000') }} 154 |
155 |
156 | Información Adicional 157 |
161 | 162 | 163 | 164 | 167 | 176 | 177 | {% if isInvoice and doc.detraccion %} 178 | 179 | 182 | 185 | 186 | 187 | 190 | 193 | 194 | 195 | 198 | 201 | 202 | {% endif %} 203 | {% if isNota %} 204 | 205 | 208 | 211 | 212 | {% endif %} 213 | {% if params.user.extras is defined %} 214 | {% for item in params.user.extras %} 215 | 216 | 219 | 222 | 223 | {% endfor %} 224 | {% endif %} 225 | 226 |
165 | LEYENDA: 166 | 168 |

169 | {% for leg in doc.legends %} 170 | {% if leg.code != '1000' %} 171 | {{ leg.value }}
172 | {% endif %} 173 | {% endfor %} 174 |

175 |
180 | TIPO DE DETRACCIÓN: 181 | 183 | {{ doc.detraccion.codBienDetraccion }} - {{ doc.detraccion.codBienDetraccion|catalog('54') }} 184 |
188 | MEDIO DE PAGO: 189 | 191 | {{ doc.detraccion.codMedioPago }} - {{ doc.detraccion.codMedioPago|catalog('59') }} 192 |
196 | N° CTA. BANCO NAC: 197 | 199 | {{ doc.detraccion.ctaBanco }} 200 |
206 | MOTIVO DE EMISIÓN: 207 | 209 | {{ doc.desMotivo }} 210 |
217 | {{ item.name|upper }}: 218 | 220 | {{ item.value }} 221 |
227 | {% if isAnticipo %} 228 | 229 | 230 | 231 | 236 | 237 | 238 |
232 |
233 | Anticipo 234 |
235 |
239 | 240 | 241 | 242 | 243 | 244 | 245 | {% for atp in doc.anticipos %} 246 | 247 | 248 | 249 | 250 | {% endfor %} 251 | 252 |
Nro. Doc.Total
{{ atp.nroDocRel }}{{ moneda }} {{ atp.total|n_format }}
253 | {% endif %} 254 | {% if doc.cuotas %} 255 | 256 | 257 | 258 | 263 | 264 | 265 |
259 |
260 | Cuotas 261 |
262 |
266 | 267 | 268 | 269 | 270 | 271 | 272 | {% for cuota in doc.cuotas %} 273 | 274 | 275 | 276 | 277 | {% endfor %} 278 | 279 |
MontoFecha Pago
{{ moneda }} {{ cuota.monto|n_format }}{{ cuota.fechaPago|date('d/m/Y') }}
280 | {% endif %} 281 |
283 |
284 | 285 | 286 | {% if isAnticipo %} 287 | 288 | 289 | 290 | 291 | {% endif %} 292 | {% if doc.mtoOperGravadas %} 293 | 294 | 295 | 296 | 297 | {% endif %} 298 | {% if doc.mtoOperInafectas %} 299 | 300 | 301 | 302 | 303 | {% endif %} 304 | {% if doc.mtoOperExoneradas %} 305 | 306 | 307 | 308 | 309 | {% endif %} 310 | {% if doc.mtoOperGratuitas %} 311 | 312 | 313 | 314 | 315 | {% endif %} 316 | {% if doc.mtoOperExportacion %} 317 | 318 | 319 | 320 | 321 | {% endif %} 322 | {% if doc.mtoBaseIvap %} 323 | 324 | 325 | 326 | 327 | {% endif %} 328 | {% if doc.mtoIGVGratuitas %} 329 | 330 | 331 | 332 | 333 | {% endif %} 334 | 335 | 336 | 337 | 338 | {% if doc.mtoIvap %} 339 | 340 | 341 | 342 | 343 | {% endif %} 344 | {% if doc.icbper %} 345 | 346 | 347 | 348 | 349 | {% endif %} 350 | {% if doc.mtoISC %} 351 | 352 | 353 | 354 | 355 | {% endif %} 356 | {% if doc.sumOtrosCargos %} 357 | 358 | 359 | 360 | 361 | {% endif %} 362 | {% if doc.mtoOtrosTributos %} 363 | 364 | 365 | 366 | 367 | {% endif %} 368 | {% if doc.redondeo %} 369 | 370 | 371 | 372 | 373 | {% endif %} 374 | 375 | 376 | 377 | 378 | {% if doc.perception and doc.perception.mto %} 379 | {% set perc = doc.perception %} 380 | {% set soles = 'PEN'|catalog('02') %} 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | {% endif %} 390 | {% if isInvoice and doc.detraccion %} 391 | 392 | 393 | 394 | 395 | {% endif %} 396 | 397 |
Total Anticipo:{{ moneda }} {{ doc.totalAnticipos|n_format }}
Op. Gravadas:{{ moneda }} {{ doc.mtoOperGravadas|n_format }}
Op. Inafectas:{{ moneda }} {{ doc.mtoOperInafectas|n_format }}
Op. Exoneradas:{{ moneda }} {{ doc.mtoOperExoneradas|n_format }}
Op. Gratuitas:{{ moneda }} {{ doc.mtoOperGratuitas|n_format }}
Op. Exportación:{{ moneda }} {{ doc.mtoOperExportacion|n_format }}
Base IVAP:{{ moneda }} {{ doc.mtoBaseIvap|n_format }}
IGV Gratuito:{{ moneda }} {{ doc.mtoIGVGratuitas|n_format }}
I.G.V.{% if params.user.numIGV is defined %} {{ params.user.numIGV }}%{% endif %}:{{ moneda }} {{ doc.mtoIGV|n_format }}
IVAP:{{ moneda }} {{ doc.mtoIvap|n_format }}
I.C.B.P.E.R.:{{ moneda }} {{ doc.icbper|n_format }}
I.S.C.:{{ moneda }} {{ doc.mtoISC|n_format }}
Otros Cargos:{{ moneda }} {{ doc.sumOtrosCargos|n_format }}
Otros Tributos:{{ moneda }} {{ doc.mtoOtrosTributos|n_format }}
Redondeo:{{ moneda }} {{ doc.redondeo|n_format }}
Precio Venta:{{ moneda }} {{ doc.mtoImpVenta|n_format }}
Percepción:{{ soles }} {{ perc.mto|n_format }}
Total a Pagar:{{ soles }} {{ perc.mtoTotal|n_format }}
Detracción ({{doc.detraccion.percent}}%):{{ moneda }} {{ doc.detraccion.mount|n_format }}
398 |
401 |
402 |
403 | {% if max_items is defined and doc.details|length > max_items %} 404 |
405 | {% endif %} 406 |
407 |
408 | 409 | 410 | 411 | 422 | 425 | 426 | 427 |
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 |
423 | Qr Image 424 |
428 |
429 |
432 | 433 | --------------------------------------------------------------------------------