<?php

namespace App\Http\Controllers\Sales;

use SoapClient;
use App\Helpers\Helper;
use App\Models\Base\Pac;
use App\Helpers\PacHelper;
use App\Helpers\BaseHelper;
use Illuminate\Support\Str;
use App\Models\Base\Company;
use App\Models\Catalogs\Tax;
use Illuminate\Http\Request;
use App\Helpers\Cfdi33Helper;
use App\Models\Sales\Customer;
use App\Mail\SendCustomerLease;
use Illuminate\Validation\Rule;
use App\Models\Catalogs\CfdiUse;
use App\Models\Catalogs\Product;
use App\Models\Catalogs\Project;
use App\Models\Base\BranchOffice;
use App\Models\Base\CfdiDownload;
use App\Models\Catalogs\Currency;
use App\Models\Sales\Salesperson;
use App\Models\Catalogs\PaymentWay;
use App\Models\Catalogs\SatProduct;
use App\Models\Catalogs\TaxRegimen;
use App\Http\Controllers\Controller;
use App\Models\Catalogs\PaymentTerm;
use App\Models\Catalogs\UnitMeasure;
use Maatwebsite\Excel\Facades\Excel;
use App\Exports\CustomerLeasesExport;
use App\Models\Catalogs\CfdiRelation;
use App\Models\Sales\CustomerPayment;
use Illuminate\Support\Facades\Crypt;
use App\Models\Catalogs\PaymentMethod;
use Symfony\Component\Process\Process;
use Illuminate\Support\Facades\Storage;
use App\Models\Sales\CustomerInvoiceTax;
use App\Models\Sales\CustomerInvoiceCfdi;
use App\Models\Sales\CustomerInvoiceLine;
use App\Models\Catalogs\ReasonCancellation;
use SimpleSoftwareIO\QrCode\Facades\QrCode;
use App\Models\Sales\CustomerInvoiceRelation;
use Illuminate\Validation\ValidationException;
use App\Models\Sales\CustomerInvoiceLineComplement;
use App\Models\Sales\CustomerInvoice as CustomerLease;
use Symfony\Component\Process\Exception\ProcessFailedException;

class CustomerLeaseController extends Controller
{
    private $list_status = [];
    private $document_type_code = 'customer.lease';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->list_status = [
            CustomerLease::DRAFT => __('sales/customer_lease.text_status_draft'),
            CustomerLease::OPEN => __('sales/customer_lease.text_status_open'),
            CustomerLease::TO_PAY => __('sales/customer_lease.text_status_to_pay'),
            CustomerLease::PAID => __('sales/customer_lease.text_status_paid'),
            CustomerLease::CANCEL => __('sales/customer_lease.text_status_cancel'),
            CustomerLease::CANCEL_PER_AUTHORIZED => __('sales/customer_lease.text_status_cancel_per_authorized'),
        ];
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        //Variables
        $limit = ($request->has('limit') ? $request->get('limit') : 100);
        $salespersons = Salesperson::populateSelect()->pluck('name', 'id');
        $branch_offices = BranchOffice::populateSelect()->pluck('name', 'id');
        $list_status = $this->list_status;
        if (empty($request->filter_date_from)) {
            $request->request->add([
                'filter_date_from' => Helper::date(\Date::parse('first day of this month'))
            ]);
        }
        if (empty($request->filter_date_to)) {
            $request->request->add([
                'filter_date_to' => Helper::date(\Date::parse('last day of this month'))
            ]);
        }
        $request->request->add(['filter_document_type_code' => $this->document_type_code]); //Filtra tipo de documento

        //Consulta
        $results = CustomerLease::filter($request->all())
            ->with('customerInvoiceCfdi')
            ->with('customer')
            ->with('salesperson')
            ->with('currency')
            ->sortable(['date' => 'desc'])->paginate($limit);

        //Vista
        return view('sales.customer_leases.index',
            compact('results', 'salespersons', 'branch_offices', 'list_status'));
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create(Request $request)
    {
        $salespersons = Salesperson::populateSelect()->pluck('name', 'id');
        $branch_offices = BranchOffice::populateSelect()->pluck('name', 'id');
        $currencies = Currency::populateSelect()->get()->pluck('name_sat', 'id');
        $payment_terms = PaymentTerm::populateSelect()->pluck('name', 'id');
        $payment_ways = PaymentWay::populateSelect()->get()->pluck('name_sat', 'id');
        $payment_methods = PaymentMethod::populateSelect()->get()->pluck('name_sat', 'id');
        $cfdi_uses = CfdiUse::populateSelect()->get()->pluck('name_sat', 'id');
        $taxes = Tax::populateSelect()->pluck('name', 'id');
        $cfdi_relations = CfdiRelation::populateSelect()->get()->pluck('name_sat', 'id');
        $projects = Project::populateSelect()->pluck('name', 'id');
        $company = Helper::defaultCompany(); //Empresa
        if(!empty($company->tax_regimen_id2)){
            $tax_regimens = TaxRegimen::populateSelect()->whereIn('id',[$company->tax_regimen_id, $company->tax_regimen_id2])->get()->pluck('name_sat', 'id');
        }else{
            $tax_regimens = collect([]);
        }
        $tax_regimen_customers = TaxRegimen::populateSelect()->get()->pluck('name_sat', 'id');
        $tax_objects = __('general.text_tax_objects');

        $duplicate_ci = null;
        if($request->duplicate_id){
            $duplicate_ci = CustomerLease::findOrFail($request->duplicate_id);
        }

        return view('sales.customer_leases.create',
            compact('salespersons', 'branch_offices', 'currencies', 'payment_ways', 'payment_terms', 'payment_methods',
                'cfdi_uses', 'taxes', 'cfdi_relations','projects','tax_regimens','tax_regimen_customers','tax_objects', 'duplicate_ci'));
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {

        //Validacion
        $this->validation($request);

        //Almacenamiento de archivos
        $customer_lease = null;

        \DB::connection('tenant')->beginTransaction();
        try {
            //Logica
            $company = Helper::defaultCompany(); //Empresa
            $request->merge(['created_uid' => \Auth::user()->id]);
            $request->merge(['updated_uid' => \Auth::user()->id]);
            $request->merge(['status' => isset($request->pre_invoice) ? CustomerLease::DRAFT : CustomerLease::OPEN]);
            $request->merge(['company_id' => $company->id]);
            //Ajusta fecha y genera fecha de vencimiento
            $date = Helper::createDateTime($request->date);
            $request->merge(['date' => Helper::dateTimeToSql($date)]);
            $date_due = $date;
            $date_due_fix = $request->date_due;//Fix valida si la fecha de vencimiento esta vacia en caso de error
            if (!empty($request->date_due)) {
                $date_due = Helper::createDate($request->date_due);
            } else {
                $payment_term = PaymentTerm::findOrFail($request->payment_term_id);
                $date_due = $payment_term->days > 0 ? $date->copy()->addDays($payment_term->days) : $date->copy();
            }
            $request->merge(['date_due' => Helper::dateToSql($date_due)]);

            //Obtiene folio
            $document_type = Helper::getNextDocumentTypeByCode($this->document_type_code,$company->id,isset($request->pre_invoice) ? true : false,$request->branch_office_id);
            $request->merge(['document_type_id' => $document_type['id']]);
            $request->merge(['name' => $document_type['name']]);
            $request->merge(['serie' => $document_type['serie']]);
            $request->merge(['folio' => $document_type['folio']]);

            //Valida que tenga folios disponibles
            if(BaseHelper::getAvailableFolios()<=0 && !isset($request->pre_invoice)){
                throw new \Exception(__('general.error_available_folios'));
            }

            //Guardar
            //Registro principal
            $customer_lease = CustomerLease::create($request->input());

            //Registro de lineas
            $amount_discount = 0;
            $amount_untaxed = 0;
            $amount_tax = 0;
            $amount_tax_ret = 0;
            $amount_total = 0;
            $balance = 0;
            $taxes = array();
            //Lineas
            if (!empty($request->item)) {
                foreach ($request->item as $key => $item) {
                    //Logica
                    $item_quantity = (double)$item['quantity'];
                    $item_price_unit = (double)$item['price_unit'];
                    $item_discount = (double)$item['discount'];
                    $item_price_reduce = ($item_price_unit * (100 - $item_discount) / 100);
                    $item_amount_untaxed = round($item_quantity * $item_price_reduce, 2);
                    $item_amount_discount = round($item_quantity * $item_price_unit, 2) - $item_amount_untaxed;
                    $item_amount_tax = 0;
                    $item_amount_tax_ret = 0;
                    if (!empty($item['taxes'])) {
                        foreach ($item['taxes'] as $tax_id) {
                            if (!empty($tax_id)) {
                                $tax = Tax::findOrFail($tax_id);
                                $tmp = 0;
                                if ($tax->factor == 'Tasa') {
                                    $tmp = $item_amount_untaxed * $tax->rate / 100;
                                } elseif ($tax->factor == 'Cuota') {
                                    $tmp = $tax->rate;
                                }
                                $tmp = round($tmp, 2);
                                if ($tax->type == 'R') { //Retenciones
                                    $item_amount_tax_ret += $tmp;
                                } else { //Traslados
                                    $item_amount_tax += $tmp;
                                }

                                //Sumatoria de impuestos
                                $taxes[$tax_id] = array(
                                    'amount_base' => $item_amount_untaxed + (isset($taxes[$tax_id]['amount_base']) ? $taxes[$tax_id]['amount_base'] : 0),
                                    'amount_tax' => $tmp + (isset($taxes[$tax_id]['amount_tax']) ? $taxes[$tax_id]['amount_tax'] : 0),
                                );
                            }
                        }
                    }
                    $item_amount_total = $item_amount_untaxed + $item_amount_tax + $item_amount_tax_ret;
                    //Sumatoria totales
                    $amount_discount += $item_amount_discount;
                    $amount_untaxed += $item_amount_untaxed;
                    $amount_tax += $item_amount_tax;
                    $amount_tax_ret += $item_amount_tax_ret;
                    $amount_total += $item_amount_total;

                    //Guardar linea
                    $customer_lease_line = CustomerInvoiceLine::create([
                        'created_uid' => \Auth::user()->id,
                        'updated_uid' => \Auth::user()->id,
                        'customer_invoice_id' => $customer_lease->id,
                        'name' => $item['name'],
                        'product_id' => $item['product_id'],
                        'sat_product_id' => $item['sat_product_id'],
                        'unit_measure_id' => $item['unit_measure_id'],
                        'quantity' => $item_quantity,
                        'price_unit' => $item_price_unit,
                        'discount' => $item_discount,
                        'price_reduce' => $item_price_reduce,
                        'amount_discount' => $item_amount_discount,
                        'amount_untaxed' => $item_amount_untaxed,
                        'amount_tax' => $item_amount_tax,
                        'amount_tax_ret' => $item_amount_tax_ret,
                        'amount_total' => $item_amount_total,
                        'sort_order' => $key,
                        'status' => 1,
                        'tax_object' => !empty($item['tax_object']) ? $item['tax_object'] : (empty($item['taxes']) ? '01' : '02'),
                    ]);

                    //Guardar impuestos por linea
                    if (!empty($item['taxes'])) {
                        $customer_lease_line->taxes()->sync($item['taxes']);
                    } else {
                        $customer_lease_line->taxes()->sync([]);
                    }

                    //Numero predial
                    $customer_invoice_line_complement = CustomerInvoiceLineComplement::create([
                        'created_uid' => \Auth::user()->id,
                        'updated_uid' => \Auth::user()->id,
                        'customer_invoice_line_id' => $customer_lease_line->id,
                        'name' => $item['name'],
                        'numero_predial' => !empty($item['numero_predial']) ? $item['numero_predial'] : '',
                        'sort_order' => $key,
                        'status' => 1,
                    ]);
                }
            }

            //Resumen de impuestos
            if (!empty($taxes)) {
                $i = 0;
                foreach ($taxes as $tax_id => $result) {
                    $tax = Tax::findOrFail($tax_id);
                    $customer_lease_tax = CustomerInvoiceTax::create([
                        'created_uid' => \Auth::user()->id,
                        'updated_uid' => \Auth::user()->id,
                        'customer_invoice_id' => $customer_lease->id,
                        'name' => $tax->name,
                        'tax_id' => $tax_id,
                        'amount_base' => $result['amount_base'],
                        'amount_tax' => $result['amount_tax'],
                        'sort_order' => $i,
                        'status' => 1,
                    ]);
                    $i++;
                }
            }

            //Cfdi relacionados
            if (!empty($request->item_relation)) {
                foreach ($request->item_relation as $key => $result) {
                    //Guardar
                    $customer_lease_relation = CustomerInvoiceRelation::create([
                        'created_uid' => \Auth::user()->id,
                        'updated_uid' => \Auth::user()->id,
                        'customer_invoice_id' => $customer_lease->id,
                        'relation_id' => !empty($result['relation_id']) ? $result['relation_id'] : null,
                        'sort_order' => $key,
                        'status' => 1,
                        'uuid_related' => $result['uuid_related'],
                    ]);
                }
            }

            //Valida que tenga exista la clase de facturacion
            $class_cfdi = setting('cfdi_version');
            if (empty($class_cfdi)) {
                throw new \Exception(__('general.error_cfdi_version'));
            }
            if (!method_exists($this, $class_cfdi)) {
                throw new \Exception(__('general.error_cfdi_class_exists'));
            }

            //Registros de cfdi
            $customer_lease_cfdi = CustomerInvoiceCfdi::create([
                'created_uid' => \Auth::user()->id,
                'updated_uid' => \Auth::user()->id,
                'customer_invoice_id' => $customer_lease->id,
                'name' => $customer_lease->name,
                'cfdi_version' => $class_cfdi,
                'status' => 1,
            ]);

            //Actualiza registro principal con totales
            $customer_lease->amount_discount = $amount_discount;
            $customer_lease->amount_untaxed = $amount_untaxed;
            $customer_lease->amount_tax = $amount_tax;
            $customer_lease->amount_tax_ret = $amount_tax_ret;
            $customer_lease->amount_total = $amount_total;
            $customer_lease->balance = $amount_total;
            $customer_lease->update();

            //Solo guarda los datos sin timbrar en caso de pre-factura
            if(isset($request->pre_invoice)) {
                //Mensaje
                flash(__('general.text_success_pre_customer_lease'))->success();

            }else{ //Crear CFDI
                //Valida Empresa y PAC para timbrado
                PacHelper::validateSatActions($company);

                //Crear XML y timbra
                $tmp = $this->$class_cfdi($customer_lease);

                //Guardar registros de CFDI
                $customer_lease_cfdi->fill(array_only($tmp, [
                    'pac_id',
                    'cfdi_version',
                    'uuid',
                    'date',
                    'file_xml',
                    'file_xml_pac',
                ]));
                $customer_lease_cfdi->save();

                //Disminuye folios
                BaseHelper::decrementFolios();

                //Mensaje
                flash(__('general.text_success_customer_lease_cfdi'))->success();
            }

            \DB::connection('tenant')->commit();

            if(!isset($request->pre_invoice)) {
                $this->saveCfdiDownloads($customer_lease, $customer_lease_cfdi);
            }


        } catch (\Exception $e) {
            //Fix fechas
            $request->merge([
                'date' => Helper::convertSqlToDateTime($request->date),
            ]);
            if (!empty($date_due_fix)) {
                $request->merge([
                    'date_due' => Helper::convertSqlToDate($request->date_due),
                ]);
            }else{
                $request->merge([
                    'date_due' => '',
                ]);
            }

            \DB::connection('tenant')->rollback();
            $company = Helper::defaultCompany(); //Empresa
            \Log::error('(' . $company->taxid . ') ' . $e->getMessage());
            flash((isset($request->pre_invoice) ? '' : __('general.error_cfdi_pac').'<br>').$e->getMessage())->error();
            return back()->withInput();
        }

        //Almacenamiento dropbox
        self::dropboxBackup($customer_lease);

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Display the specified resource.
     *
     * @param  \App\Models\Sales\CustomerLease $customer_lease
     * @return \Illuminate\Http\Response
     */
    public function show(CustomerLease $customer_lease)
    {
        //Datos
        $data = [];

        //Si tiene CFDI obtiene la informacion de los nodos
        if(!empty($customer_lease->customerInvoiceCfdi->file_xml_pac) && !empty($customer_lease->customerInvoiceCfdi->uuid)){

            $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
            $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;

            //Valida que el archivo exista
            if(!empty($customer_lease->customerInvoiceCfdi->file_xml_pac) && \Storage::exists($file_xml_pac)) {
                $cfdi = \CfdiUtils\Cfdi::newFromString(\Storage::get($file_xml_pac));
                $data = Cfdi33Helper::getQuickArrayCfdi($cfdi);

                //Genera codigo QR
                $image = QrCode::format('png')->size(150)->margin(0)->generate($data['qr_cadena']);
                $data['qr'] = 'data:image/png;base64,' . base64_encode($image);

                //Regimen fiscal
                $data['tax_regimen'] = TaxRegimen::where('code', $data['cfdi33']->emisor['RegimenFiscal'])->first()->name_sat;
                $data['tax_regimen_customer'] = !empty($data['cfdi33']->receptor['RegimenFiscalReceptor']) ? TaxRegimen::where('code', $data['cfdi33']->receptor['RegimenFiscalReceptor'])->first()->name_sat : '';
            }
        }

        return view('sales.customer_leases.show', compact('customer_lease','data'));
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  \App\Models\Sales\CustomerLease $customer_lease
     * @return \Illuminate\Http\Response
     */
    public function edit(CustomerLease $customer_lease)
    {
        $salespersons = Salesperson::populateSelect()->pluck('name', 'id');
        $branch_offices = BranchOffice::populateSelect()->pluck('name', 'id');
        $currencies = Currency::populateSelect()->get()->pluck('name_sat', 'id');
        $payment_terms = PaymentTerm::populateSelect()->pluck('name', 'id');
        $payment_ways = PaymentWay::populateSelect()->get()->pluck('name_sat', 'id');
        $payment_methods = PaymentMethod::populateSelect()->get()->pluck('name_sat', 'id');
        $cfdi_uses = CfdiUse::populateSelect()->get()->pluck('name_sat', 'id');
        $taxes = Tax::populateSelect()->pluck('name', 'id');
        $cfdi_relations = CfdiRelation::populateSelect()->get()->pluck('name_sat', 'id');
        $projects = Project::populateSelect()->pluck('name', 'id');
        $company = $customer_lease->company; //Empresa
        if(!empty($company->tax_regimen_id2)){
            $tax_regimens = TaxRegimen::populateSelect()->whereIn('id',[$company->tax_regimen_id, $company->tax_regimen_id2])->get()->pluck('name_sat', 'id');
        }else{
            $tax_regimens = collect([]);
        }
        $tax_regimen_customers = TaxRegimen::populateSelect()->get()->pluck('name_sat', 'id');
        $tax_objects = __('general.text_tax_objects');

        return view('sales.customer_leases.edit',
            compact('customer_lease','salespersons', 'branch_offices', 'currencies', 'payment_ways', 'payment_terms', 'payment_methods',
                'cfdi_uses', 'taxes', 'cfdi_relations','projects','tax_regimens','tax_regimen_customers','tax_objects'));
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \App\Models\Sales\CustomerLease $customer_lease
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, CustomerLease $customer_lease)
    {
        //Validacion
        $this->validation($request);

        \DB::connection('tenant')->beginTransaction();
        try {

            //Logica
            $request->merge(['created_uid' => \Auth::user()->id]);
            $request->merge(['updated_uid' => \Auth::user()->id]);
            $request->merge(['status' => isset($request->pre_invoice) ? CustomerLease::DRAFT : CustomerLease::OPEN]);
            //Ajusta fecha y genera fecha de vencimiento
            $date = Helper::createDateTime($request->date);
            $request->merge(['date' => Helper::dateTimeToSql($date)]);
            $date_due = $date;
            $date_due_fix = $request->date_due;//Fix valida si la fecha de vencimiento esta vacia en caso de error
            if (!empty($request->date_due)) {
                $date_due = Helper::createDate($request->date_due);
            } else {
                $payment_term = PaymentTerm::findOrFail($request->payment_term_id);
                $date_due = $payment_term->days > 0 ? $date->copy()->addDays($payment_term->days) : $date->copy();
            }
            $request->merge(['date_due' => Helper::dateToSql($date_due)]);
            $customer_lease->fill($request->only([
                'updated_uid',
                'date',
                'date_due',
                'reference',
                'customer_id',
                'branch_office_id',
                'payment_term_id',
                'payment_way_id',
                'payment_method_id',
                'cfdi_use_id',
                'salesperson_id',
                'currency_id',
                'currency_value',
                'cfdi_relation_id',
                'comment',
                'status',
                'project_id',
                'tax_regimen_id',
                'tax_regimen_customer_id'
            ]));

            //Guardar
            //Registro principal
            $customer_lease->save();

            //Actualiza folios
            if(!isset($request->pre_invoice)) {
                //Valida que tenga folios disponibles
                if(BaseHelper::getAvailableFolios()<=0){
                    throw new \Exception(__('general.error_available_folios'));
                }

                //Obtiene folio
                $document_type = Helper::getNextDocumentTypeByCode($this->document_type_code, $customer_lease->company->id, false, $customer_lease->branch_office_id);
                $customer_lease->draft = $customer_lease->name;
                $customer_lease->name = $document_type['name'];
                $customer_lease->serie = $document_type['serie'];
                $customer_lease->folio = $document_type['folio'];
                $customer_lease->save();
            }

            //Elimina partidas
            if (!empty($request->delete_item)) {
                foreach ($request->delete_item as $key => $result) {
                    //Actualizar status
                    $customer_lease_line = CustomerInvoiceLine::findOrFail($result);
                    $customer_lease_line->updated_uid = \Auth::user()->id;
                    $customer_lease_line->status = 0;
                    $customer_lease_line->save();
                }
            }
            //Registro de lineas
            $amount_discount = 0;
            $amount_untaxed = 0;
            $amount_tax = 0;
            $amount_tax_ret = 0;
            $amount_total = 0;
            $balance = 0;
            $taxes = array();
            //Lineas
            if (!empty($request->item)) {
                foreach ($request->item as $key => $item) {
                    //Logica
                    $item_quantity = (double)$item['quantity'];
                    $item_price_unit = (double)$item['price_unit'];
                    $item_discount = (double)$item['discount'];
                    $item_price_reduce = ($item_price_unit * (100 - $item_discount) / 100);
                    $item_amount_untaxed = round($item_quantity * $item_price_reduce, 2);
                    $item_amount_discount = round($item_quantity * $item_price_unit, 2) - $item_amount_untaxed;
                    $item_amount_tax = 0;
                    $item_amount_tax_ret = 0;
                    if (!empty($item['taxes'])) {
                        foreach ($item['taxes'] as $tax_id) {
                            if (!empty($tax_id)) {
                                $tax = Tax::findOrFail($tax_id);
                                $tmp = 0;
                                if ($tax->factor == 'Tasa') {
                                    $tmp = $item_amount_untaxed * $tax->rate / 100;
                                } elseif ($tax->factor == 'Cuota') {
                                    $tmp = $tax->rate;
                                }
                                $tmp = round($tmp, 2);
                                if ($tax->type == 'R') { //Retenciones
                                    $item_amount_tax_ret += $tmp;
                                } else { //Traslados
                                    $item_amount_tax += $tmp;
                                }

                                //Sumatoria de impuestos
                                $taxes[$tax_id] = array(
                                    'amount_base' => $item_amount_untaxed + (isset($taxes[$tax_id]['amount_base']) ? $taxes[$tax_id]['amount_base'] : 0),
                                    'amount_tax' => $tmp + (isset($taxes[$tax_id]['amount_tax']) ? $taxes[$tax_id]['amount_tax'] : 0),
                                );
                            }
                        }
                    }
                    $item_amount_total = $item_amount_untaxed + $item_amount_tax + $item_amount_tax_ret;
                    //Sumatoria totales
                    $amount_discount += $item_amount_discount;
                    $amount_untaxed += $item_amount_untaxed;
                    $amount_tax += $item_amount_tax;
                    $amount_tax_ret += $item_amount_tax_ret;
                    $amount_total += $item_amount_total;

                    //Guardar linea
                    $data = [
                        'created_uid' => \Auth::user()->id,
                        'updated_uid' => \Auth::user()->id,
                        'customer_invoice_id' => $customer_lease->id,
                        'name' => $item['name'],
                        'product_id' => $item['product_id'],
                        'sat_product_id' => $item['sat_product_id'],
                        'unit_measure_id' => $item['unit_measure_id'],
                        'quantity' => $item_quantity,
                        'price_unit' => $item_price_unit,
                        'discount' => $item_discount,
                        'price_reduce' => $item_price_reduce,
                        'amount_discount' => $item_amount_discount,
                        'amount_untaxed' => $item_amount_untaxed,
                        'amount_tax' => $item_amount_tax,
                        'amount_tax_ret' => $item_amount_tax_ret,
                        'amount_total' => $item_amount_total,
                        'sort_order' => $key,
                        'status' => 1,
                        'tax_object' => !empty($item['tax_object']) ? $item['tax_object'] : (empty($item['taxes']) ? '01' : '02'),
                    ];
                    if (!empty($item['id'])) {
                        $customer_lease_line = CustomerInvoiceLine::findOrFail($item['id']);
                        $customer_lease_line->fill(array_only($data, [
                            'updated_uid',
                            'name',
                            'product_id',
                            'sat_product_id',
                            'unit_measure_id',
                            'quantity',
                            'price_unit',
                            'discount',
                            'price_reduce',
                            'amount_discount',
                            'amount_untaxed',
                            'amount_tax',
                            'amount_tax_ret',
                            'amount_total',
                            'sort_order',
                            'tax_object'
                        ]));
                        $customer_lease_line->save();
                    }else{
                        $customer_lease_line = CustomerInvoiceLine::create($data);
                    }

                    //Guardar impuestos por linea
                    if (!empty($item['taxes'])) {
                        $customer_lease_line->taxes()->sync($item['taxes']);
                    } else {
                        $customer_lease_line->taxes()->sync([]);
                    }

                    //Numero predial
                    $customer_invoice_line_complement = CustomerInvoiceLineComplement::where('customer_invoice_line_id','=',$customer_lease_line->id)->first();
                    $data_complement = [
                        'created_uid' => \Auth::user()->id,
                        'updated_uid' => \Auth::user()->id,
                        'customer_invoice_line_id' => $customer_lease_line->id,
                        'name' => $item['name'],
                        'numero_predial' => !empty($item['numero_predial']) ? $item['numero_predial'] : '',
                        'sort_order' => $key,
                        'status' => 1,
                    ];
                    if(!empty($customer_invoice_line_complement)){
                        $customer_invoice_line_complement->fill(array_only($data_complement, [
                            'updated_uid',
                            'name',
                            'numero_predial',
                            'sort_order',
                        ]));
                        $customer_invoice_line_complement->save();
                    }else{
                        $customer_invoice_line_complement = CustomerInvoiceLineComplement::create($data_complement);
                    }
                }
            }

            //Resumen de impuestos
            CustomerInvoiceTax::where('customer_invoice_id','=',$customer_lease->id)->delete(); //Borra todo e inserta nuevamente
            if (!empty($taxes)) {
                $i = 0;
                foreach ($taxes as $tax_id => $result) {
                    $tax = Tax::findOrFail($tax_id);
                    $customer_lease_tax = CustomerInvoiceTax::create([
                        'created_uid' => \Auth::user()->id,
                        'updated_uid' => \Auth::user()->id,
                        'customer_invoice_id' => $customer_lease->id,
                        'name' => $tax->name,
                        'tax_id' => $tax_id,
                        'amount_base' => $result['amount_base'],
                        'amount_tax' => $result['amount_tax'],
                        'sort_order' => $i,
                        'status' => 1,
                    ]);
                    $i++;
                }
            }

            //Cfdi relacionados
            CustomerInvoiceRelation::where('customer_invoice_id','=',$customer_lease->id)->delete(); //Borra todo e inserta nuevamente
            if (!empty($request->item_relation)) {
                foreach ($request->item_relation as $key => $result) {
                    //Guardar
                    $customer_lease_relation = CustomerInvoiceRelation::create([
                        'created_uid' => \Auth::user()->id,
                        'updated_uid' => \Auth::user()->id,
                        'customer_invoice_id' => $customer_lease->id,
                        'relation_id' => !empty($result['relation_id']) ? $result['relation_id'] : null,
                        'sort_order' => $key,
                        'status' => 1,
                        'uuid_related' => $result['uuid_related'],
                    ]);
                }
            }

            //Valida que tenga exista la clase de facturacion
            $class_cfdi = setting('cfdi_version');
            if (empty($class_cfdi)) {
                throw new \Exception(__('general.error_cfdi_version'));
            }
            if (!method_exists($this, $class_cfdi)) {
                throw new \Exception(__('general.error_cfdi_class_exists'));
            }

            //Registros de cfdi
            CustomerInvoiceCfdi::where('customer_invoice_id','=',$customer_lease->id)->delete(); //Borra todo e inserta nuevamente
            $customer_lease_cfdi = CustomerInvoiceCfdi::create([
                'created_uid' => \Auth::user()->id,
                'updated_uid' => \Auth::user()->id,
                'customer_invoice_id' => $customer_lease->id,
                'name' => $customer_lease->name,
                'cfdi_version' => $class_cfdi,
                'status' => 1,
            ]);

            //Actualiza registro principal con totales
            $customer_lease->amount_discount = $amount_discount;
            $customer_lease->amount_untaxed = $amount_untaxed;
            $customer_lease->amount_tax = $amount_tax;
            $customer_lease->amount_tax_ret = $amount_tax_ret;
            $customer_lease->amount_total = $amount_total;
            $customer_lease->balance = $amount_total;
            $customer_lease->update();

            //Solo guarda los datos sin timbrar en caso de pre-factura
            if(isset($request->pre_invoice)) {
                //Mensaje
                flash(__('general.text_success_pre_customer_lease'))->success();

            }else{ //Crear CFDI
                //Valida Empresa y PAC para timbrado
                PacHelper::validateSatActions($customer_lease->company);

                //Crear XML y timbra
                $tmp = $this->$class_cfdi($customer_lease);

                //Guardar registros de CFDI
                $customer_lease_cfdi->fill(array_only($tmp, [
                    'pac_id',
                    'cfdi_version',
                    'uuid',
                    'date',
                    'file_xml',
                    'file_xml_pac',
                ]));
                $customer_lease_cfdi->save();

                //Disminuye folios
                BaseHelper::decrementFolios();

                //Mensaje
                flash(__('general.text_success_customer_lease_cfdi'))->success();
            }

            \DB::connection('tenant')->commit();

            if(!isset($request->pre_invoice)) {
                $this->saveCfdiDownloads($customer_lease, $customer_lease_cfdi);
            }


        } catch (\Exception $e) {
            //Fix fechas
            $request->merge([
                'date' => Helper::convertSqlToDateTime($request->date),
            ]);
            if (!empty($date_due_fix)) {
                $request->merge([
                    'date_due' => Helper::convertSqlToDate($request->date_due),
                ]);
            }else{
                $request->merge([
                    'date_due' => '',
                ]);
            }

            \DB::connection('tenant')->rollback();
            \Log::error('(' . $customer_lease->company->taxid . ') ' . $e->getMessage());
            flash((isset($request->pre_invoice) ? '' : __('general.error_cfdi_pac').'<br>').$e->getMessage())->error();
            return back()->withInput();
        }

        //Almacenamiento dropbox
        self::dropboxBackup($customer_lease);

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  \App\Models\Sales\CustomerLease $customer_lease
     * @return \Illuminate\Http\Response
     */
    public function destroy(Request $request, CustomerLease $customer_lease)
    {
        \DB::connection('tenant')->beginTransaction();
        try {
            //Logica
            if ((int)$customer_lease->status != CustomerLease::CANCEL && $customer_lease->balance >= $customer_lease->amount_total) {
                //Actualiza status
                $customer_lease->updated_uid = \Auth::user()->id;
                $customer_lease->status = CustomerLease::CANCEL;
                //Por autorizar cuando se manda la autorizacion al buzon tributario del SAT
                if($request->cancelable == 2){
                    $customer_lease->status = CustomerLease::CANCEL_PER_AUTHORIZED;
                }
                $customer_lease->save();

                //Actualiza status CFDI
                $customer_lease->customerInvoiceCfdi->status = 0;
                $customer_lease->customerInvoiceCfdi->reason_cancellation_id = $request->reason_cancellation_id;
                $customer_lease->customerInvoiceCfdi->reason_cancellation_uuid = $request->reason_cancellation_uuid;
                $customer_lease->customerInvoiceCfdi->save();

                //Cancelacion del timbre fiscal
                if (!empty($customer_lease->customerInvoiceCfdi->cfdi_version) && !empty($customer_lease->customerInvoiceCfdi->uuid)) {
                    //Valida Empresa y PAC para cancelar timbrado
                    PacHelper::validateSatCancelActions($customer_lease->company,$customer_lease->customerInvoiceCfdi->pac);

                    //Obtener el sellos del CFDI
                    $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
                    $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;
                    $cfdi = \CfdiUtils\Cfdi::newFromString(\Storage::get($file_xml_pac));
                    $data = Cfdi33Helper::getQuickArrayCfdi($cfdi);

                    //Arreglo temporal para actualizar Customer Invoice CFDI
                    $tmp = [
                        'cancel_date' => Helper::dateTimeToSql(\Date::now()),
                        'cancel_response' => '',
                        'cancel_state' => $request->cancel_state,
                        'rfcR' => $customer_lease->customer->taxid,
                        'uuid' => $customer_lease->customerInvoiceCfdi->uuid,
                        'total' => Helper::numberFormat($customer_lease->amount_total,
                            $customer_lease->currency->decimal_place, false),
                        'cfdi_type' => $customer_lease->documentType->cfdiType->code,
                        'cfdi_version' => 'cfdi3.3',
                        'fe' => substr($data['cfdi33']->complemento->timbreFiscalDigital['SelloCFD'],-8), //Los últimos 8 caracteres del Atributo Sello del CFDI
                        'file_xml_pac' => $file_xml_pac,
                        'reason_cancellation_code' => $customer_lease->customerInvoiceCfdi->reasonCancellation->code ?? '',
                        'reason_cancellation_uuid' => $customer_lease->customerInvoiceCfdi->reason_cancellation_uuid ?? '',
                    ];

                    //Cancelar Timbrado de XML
                    $class_pac = $customer_lease->customerInvoiceCfdi->pac->code . 'Cancel';
                    $tmp = PacHelper::$class_pac($tmp,$customer_lease->company,$customer_lease->customerInvoiceCfdi->pac);

                    //Guardar registros de CFDI
                    $customer_lease->customerInvoiceCfdi->fill(array_only($tmp,[
                        'cancel_date',
                        'cancel_response',
                        'cancel_state',
                    ]));
                    $customer_lease->customerInvoiceCfdi->save();

                    $cfdi_download = CfdiDownload::where('uuid', $customer_lease->customerInvoiceCfdi->uuid)->where('type', 2)->first();
                    if(!empty($cfdi_download)){
                        $cfdi_download->status = 'Cancelado';
                        $cfdi_download->save();
                    }

                    //Disminuye folios
                    BaseHelper::decrementFolios();

                }
            }
            \DB::connection('tenant')->commit();

            //Mensaje
            flash(__('general.text_form_success_cancel'))->success();


        } catch (\Exception $e) {
            \DB::connection('tenant')->rollback();
            \Log::error('(' . $customer_lease->company->taxid . ') ' . $e->getMessage());
            flash($e->getMessage())->error();
            return redirect('/sales/customer-leases');
        }

        //Almacenamiento dropbox
        if ((int)$customer_lease->status == CustomerLease::CANCEL) {
            self::dropboxBackup($customer_lease,false,true);
        }

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Validacion de formulario
     *
     * @param Request $request
     */
    public function validation(Request $request)
    {

        $this->validate($request, [
            'customer_id' => 'required|integer',
            'branch_office_id' => 'required|integer',
            'date' => 'required|date|date_format:"'.setting('datetime_format', 'd-m-Y H:i:s').'"',
            'date_due' => 'nullable|date|date_format:"'.setting('date_format', 'd-m-Y').'"|after_or_equal:' . Helper::date(Helper::createDateTime($request->date)),
            'currency_id' => 'required|integer',
            'currency_value' => 'required|numeric|min:0.1',
            'payment_term_id' => 'required|integer',
            'payment_way_id' => 'required|integer',
            'payment_method_id' => 'required|integer',
            'cfdi_use_id' => 'required|integer',
            'cfdi_relation_id' => 'nullable|integer|required_with:item_relation',
            'item' => 'required',
            'item.*.name' => 'required',
            'item.*.unit_measure_id' => 'required',
            'item.*.sat_product_id' => 'required',
            'item.*.quantity' => 'required|numeric|min:0.00001',
            'item.*.price_unit' => 'required|numeric|min:0.00001',
            'item.*.discount' => 'nullable|numeric|min:0',
            'item_relation.*.uuid_related' => 'required',

        ], [
            'customer_id.*' => __('sales/customer_lease.error_customer_id'),
            'branch_office_id.*' => __('sales/customer_lease.error_branch_office_id'),
            'date.required' => __('sales/customer_lease.error_date'),
            'date.date' => __('sales/customer_lease.error_date_format'),
            'date.date_format' => __('sales/customer_lease.error_date_format'),
            'date_due.date' => __('sales/customer_lease.error_date_due_format'),
            'date_due.date_format' => __('sales/customer_lease.error_date_due_format'),
            'date_due.after_or_equal' => __('sales/customer_lease.error_date_due_after'),
            'currency_id.*' => __('sales/customer_lease.error_currency_id'),
            'currency_value.*' => __('sales/customer_lease.error_currency_value'),
            'payment_term_id.*' => __('sales/customer_lease.error_payment_term_id'),
            'payment_way_id.*' => __('sales/customer_lease.error_payment_way_id'),
            'payment_method_id.*' => __('sales/customer_lease.error_payment_method_id'),
            'cfdi_use_id.*' => __('sales/customer_lease.error_cfdi_use_id'),
            'cfdi_relation_id.*' => __('sales/customer_lease.error_cfdi_relation_id'),
            'item.required' => __('sales/customer_lease.error_item'),
            'item.*.name.*' => __('sales/customer_lease.error_line_name'),
            'item.*.unit_measure_id.*' => __('sales/customer_lease.error_line_unit_measure_id'),
            'item.*.sat_product_id.*' => __('sales/customer_lease.error_line_sat_product_id'),
            'item.*.quantity.*' => __('sales/customer_lease.error_line_quantity'),
            'item.*.price_unit.*' => __('sales/customer_lease.error_line_price_unit'),
            'item_relation.*.uuid_related.*' => __('sales/customer_lease.error_relation_uuid_related'),
        ]);

        //Validaciones manuales
        $validator = \Validator::make([], []);
        if(setting('cfdi_version') == 'cfdi40'){
            if (empty($request->tax_regimen_customer_id)) {
                $validator->after(function ($validator) {
                    $validator->errors()->add('tax_regimen_customer_id', __('sales/customer_lease.error_tax_regimen_customer_id'));
                });
            }
            $customer = Customer::find($request->customer_id);
            if (empty($customer->postcode) && !in_array($customer->taxid, ['XAXX010101000', 'XEXX010101000'])) {
                $validator->after(function ($validator) {
                    $validator->errors()->add('customer_id', __('sales/customer_lease.error_postcode_customer'));
                });
            }
        }

        if ($validator->fails()) {
            throw new ValidationException($validator);
        }

    }

    /**
     * Crear XML y enviar a timbrar CFDI 3.3
     *
     * @param CustomerLease $customer_lease
     * @return array|\CfdiUtils\Elements\Cfdi33\Concepto|float|int
     * @throws \Exception
     */
    private function cfdi33(CustomerLease $customer_lease)
    {

        try {
            //Logica
            $pac = Pac::findOrFail(setting('default_pac_id')); //PAC

            //Arreglo CFDI 3.3
            $cfdi33 = [];
            if (!empty($customer_lease->serie)) {
                $cfdi33['Serie'] = $customer_lease->serie;
            }
            $cfdi33['Folio'] = $customer_lease->folio;
            $cfdi33['Fecha'] = \Date::parse($customer_lease->date)->format('Y-m-d\TH:i:s');
            //$cfdi33['Sello']
            $cfdi33['FormaPago'] = $customer_lease->paymentWay->code;
            $cfdi33['NoCertificado'] = $customer_lease->company->certificate_number;
            //$cfdi33['Certificado']
            $cfdi33['CondicionesDePago'] = $customer_lease->paymentTerm->name;
            $cfdi33['SubTotal'] = Helper::numberFormat($customer_lease->amount_untaxed + $customer_lease->amount_discount,
                $customer_lease->currency->decimal_place, false);
            if($customer_lease->amount_discount>0) {
                $cfdi33['Descuento'] = Helper::numberFormat($customer_lease->amount_discount,
                    $customer_lease->currency->decimal_place, false);
            }
            $cfdi33['Moneda'] = $customer_lease->currency->code;
            if ($customer_lease->currency->code != 'MXN') {
                $cfdi33['TipoCambio'] = Helper::numberFormat($customer_lease->currency_value, 4, false);
            }
            $cfdi33['Total'] = Helper::numberFormat($customer_lease->amount_total,
                $customer_lease->currency->decimal_place, false);
            $cfdi33['TipoDeComprobante'] = $customer_lease->documentType->cfdiType->code;
            $cfdi33['MetodoPago'] = $customer_lease->paymentMethod->code;
            $cfdi33['LugarExpedicion'] = $customer_lease->branchOffice->postcode;
            if (!empty($customer_lease->confirmacion)) {
                $cfdi33['Confirmacion'] = $customer_lease->confirmacion;
            }
            //---Cfdi Relacionados
            $cfdi33_relacionados = [];
            $cfdi33_relacionado = [];
            if (!empty($customer_lease->cfdi_relation_id)) {
                $cfdi33_relacionados['TipoRelacion'] = $customer_lease->cfdiRelation->code;
                if ($customer_lease->customerInvoiceRelations->isNotEmpty()) {
                    foreach ($customer_lease->customerInvoiceRelations as $key => $result) {
                        $cfdi33_relacionado[$key] = [];
                        $cfdi33_relacionado[$key]['UUID'] = $result->uuid_related;
                    }
                }
            }
            //---Emisor
            $cfdi33_emisor = [];
            $cfdi33_emisor['Rfc'] = $customer_lease->company->taxid;
            $cfdi33_emisor['Nombre'] = trim($customer_lease->company->name);
            $cfdi33_emisor['RegimenFiscal'] = !empty($customer_lease->tax_regimen_id) ? $customer_lease->taxRegimen->code : $customer_lease->company->taxRegimen->code;
            //---Receptor
            $cfdi33_receptor = [];
            $cfdi33_receptor['Rfc'] = $customer_lease->customer->taxid;
            $cfdi33_receptor['Nombre'] = trim($customer_lease->customer->name);
            if ($customer_lease->customer->taxid == 'XEXX010101000') {
                $cfdi33_receptor['ResidenciaFiscal'] = $customer_lease->customer->country->code;
                $cfdi33_receptor['NumRegIdTrib'] = $customer_lease->customer->numid;
            }
            $cfdi33_receptor['UsoCFDI'] = $customer_lease->cfdiUse->code;
            //---Conceptos
            $cfdi33_conceptos = [];
            $cfdi33_conceptos_traslados = [];
            $cfdi33_conceptos_retenciones = [];
            $local_taxes_total = 0;
            $local_taxes_total_ret = 0;
            foreach ($customer_lease->customerActiveInvoiceLines as $key => $result) {
                $cfdi33_conceptos [$key]['ClaveProdServ'] = $result->satProduct->code;
                if (!empty($result->product->code)) {
                    $cfdi33_conceptos [$key]['NoIdentificacion'] = trim($result->product->code);
                }
                $cfdi33_conceptos [$key]['Cantidad'] = Helper::numberFormat($result->quantity, 6, false);
                $cfdi33_conceptos [$key]['ClaveUnidad'] = trim($result->unitMeasure->code);
                $cfdi33_conceptos [$key]['Unidad'] = str_limit(trim($result->unitMeasure->name),20,'');
                $cfdi33_conceptos [$key]['Descripcion'] = trim($result->name);
                $cfdi33_conceptos [$key]['ValorUnitario'] = Helper::numberFormat($result->price_unit, 6, false);
                $cfdi33_conceptos [$key]['Importe'] = Helper::numberFormat($result->amount_untaxed + $result->amount_discount,
                    2, false);
                if($result->amount_discount>0) {
                    $cfdi33_conceptos [$key]['Descuento'] = Helper::numberFormat($result->amount_discount, 2, false);
                }
                //['InformacionAduanera']
                //['CuentaPredial']
                if(!empty($result->customerInvoiceLineComplement)) {
                    if(!empty($result->customerInvoiceLineComplement->numero_predial)) {
                        $cfdi33_conceptos_compl_predial[$key] = [];
                        $cfdi33_conceptos_compl_predial[$key]['numero_predial'] = trim($result->customerInvoiceLineComplement->numero_predial);
                    }
                }
                //['ComplementoConcepto']
                //['Parte']
                //Complemento

                //Impuestos por concepto
                $cfdi33_conceptos_traslados[$key] = [];
                $cfdi33_conceptos_retenciones[$key] = [];
                if ($result->taxes) {
                    foreach ($result->taxes as $key2 => $result2) {
                        $tmp = 0;
                        $rate = $result2->rate;
                        if ($result2->factor == 'Tasa') {
                            $rate /= 100;
                            $tmp = $result->amount_untaxed * $rate;
                        } elseif ($result2->factor == 'Cuota') {
                            $tmp = $rate;
                        }
                        $tmp = round($tmp, 2);
                        if ($result2->type == 'R') { //Retenciones
                            if($result2->local_taxes) {
                                $local_taxes_total_ret += abs($tmp);
                                continue;
                            }
                            $cfdi33_conceptos_retenciones[$key][$key2] = [];
                            $cfdi33_conceptos_retenciones[$key][$key2]['Base'] = Helper::numberFormat($result->amount_untaxed,
                                2, false);
                            $cfdi33_conceptos_retenciones[$key][$key2]['Impuesto'] = $result2->code;
                            $cfdi33_conceptos_retenciones[$key][$key2]['TipoFactor'] = $result2->factor; //Para retenciones no hay exento
                            $cfdi33_conceptos_retenciones[$key][$key2]['TasaOCuota'] = Helper::numberFormat(abs($rate),
                                6, false);
                            $cfdi33_conceptos_retenciones[$key][$key2]['Importe'] = Helper::numberFormat(abs($tmp), 2,
                                false);
                        } else { //Traslados
                            if($result2->local_taxes) {
                                $local_taxes_total += abs($tmp);
                                continue;
                            }
                            $cfdi33_conceptos_traslados[$key][$key2] = [];
                            $cfdi33_conceptos_traslados[$key][$key2]['Base'] = Helper::numberFormat($result->amount_untaxed,
                                2, false);
                            $cfdi33_conceptos_traslados[$key][$key2]['Impuesto'] = $result2->code;
                            $cfdi33_conceptos_traslados[$key][$key2]['TipoFactor'] = $result2->factor; //Para retenciones no hay exento
                            if ($result2->factor != 'Exento') {
                                $cfdi33_conceptos_traslados[$key][$key2]['TasaOCuota'] = Helper::numberFormat(abs($rate),
                                    6, false);
                                $cfdi33_conceptos_traslados[$key][$key2]['Importe'] = Helper::numberFormat(abs($tmp), 2,
                                    false);
                            }
                        }
                    }
                }
            }
            //Impuestos
            $cfdi33_retenciones = [];
            $cfdi33_traslados = [];
            if ($customer_lease->customerInvoiceTaxes->isNotEmpty()) {
                foreach ($customer_lease->customerInvoiceTaxes as $key => $result) {
                    $tmp = $result->amount_tax;
                    $rate = $result->tax->rate;
                    if ($result->tax->factor == 'Tasa') {
                        $rate /= 100;
                    }
                    //Omite los impuestos locales
                    if($result->tax->local_taxes) {
                        continue;
                    }
                    if ($result->tax->type == 'R') { //Retenciones
                        $cfdi33_retenciones[$key] = [];
                        $cfdi33_retenciones[$key]['Impuesto'] = $result->tax->code;
                        $cfdi33_retenciones[$key]['Importe'] = Helper::numberFormat(abs($tmp), $customer_lease->currency->decimal_place, false);
                    } else { //Traslados
                        if ($result->tax->factor != 'Exento') {
                            $cfdi33_traslados[$key] = [];
                            $cfdi33_traslados[$key]['Impuesto'] = $result->tax->code;
                            $cfdi33_traslados[$key]['TipoFactor'] = $result->tax->factor;
                            $cfdi33_traslados[$key]['TasaOCuota'] = Helper::numberFormat(abs($rate), 6, false);
                            $cfdi33_traslados[$key]['Importe'] = Helper::numberFormat(abs($tmp), 2, false);
                        }
                    }
                }
            }
            $cfdi33_impuestos = [];
            if (abs($customer_lease->amount_tax_ret) > 0 || !empty($cfdi33_retenciones)) {
                $tmp = Helper::numberFormat(abs($customer_lease->amount_tax_ret) - $local_taxes_total_ret, $customer_lease->currency->decimal_place, false);
                if($tmp > 0 || !empty($cfdi33_retenciones)){
                    $cfdi33_impuestos['TotalImpuestosRetenidos'] = $tmp;
                }
            }
            if (abs($customer_lease->amount_tax) > 0 || !empty($cfdi33_traslados)) {
                $cfdi33_impuestos['TotalImpuestosTrasladados'] = Helper::numberFormat(abs($customer_lease->amount_tax) - $local_taxes_total,
                    $customer_lease->currency->decimal_place, false);
            }

            //Genera XML
            $certificado = new \CfdiUtils\Certificado\Certificado(\Storage::path($customer_lease->company->pathFileCer()));
            $creator = new \CfdiUtils\CfdiCreator33($cfdi33, $certificado);
            $creator->setXmlResolver(PacHelper::resourcePathCfdiUtils()); //Almacenamiento local
            $comprobante = $creator->comprobante();

            $schema_location_implocal='';
            //Impuestos locales
            if($local_taxes_total > 0 || $local_taxes_total_ret > 0) {
                $schema_location_implocal=' http://www.sat.gob.mx/implocal http://www.sat.gob.mx/sitio_internet/cfd/implocal/implocal.xsd';
            }
            if(!empty($schema_location_implocal)){
                $comprobante->addAttributes([
                    'xsi:schemaLocation' => 'http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd' . $schema_location_implocal,
                ]);
                if(!empty($schema_location_implocal)){
                    $comprobante->addAttributes([
                        'xmlns:implocal' => 'http://www.sat.gob.mx/implocal'
                    ]);
                }
            }

            if (!empty($cfdi33_relacionados)) {
                $comprobante->addCfdiRelacionados($cfdi33_relacionados);
            }
            if (!empty($cfdi33_relacionado)) {
                foreach ($cfdi33_relacionado as $key => $result) {
                    $comprobante->addCfdiRelacionado($result);
                }
            }
            $comprobante->addEmisor($cfdi33_emisor);
            $comprobante->addReceptor($cfdi33_receptor);
            //Conceptos
            foreach ($cfdi33_conceptos as $key => $result) {
                $concepto = $comprobante->addConcepto($result);
                if (!empty($cfdi33_conceptos_traslados[$key])) {
                    foreach ($cfdi33_conceptos_traslados[$key] as $result2) {
                        $concepto->multiTraslado($result2);
                    }
                }
                if (!empty($cfdi33_conceptos_retenciones[$key])) {
                    foreach ($cfdi33_conceptos_retenciones[$key] as $result2) {
                        $concepto->multiRetencion($result2);
                    }
                }
                //Numero predial
                if(!empty($cfdi33_conceptos_compl_predial[$key]['numero_predial'])) {
                    $concepto->addCuentaPredial(['Numero' => $cfdi33_conceptos_compl_predial[$key]['numero_predial']]);
                }
            }
            //Impuestos
            if(!empty($cfdi33_impuestos)) {
                $comprobante->addImpuestos($cfdi33_impuestos);
                if (!empty($cfdi33_retenciones)) {
                    foreach ($cfdi33_retenciones as $result2) {
                        $comprobante->multiRetencion($result2);
                    }
                }
                if (!empty($cfdi33_traslados)) {
                    foreach ($cfdi33_traslados as $result2) {
                        $comprobante->multiTraslado($result2);
                    }
                }
            }

            //Complemento de impuestos locales
            if($local_taxes_total > 0 || $local_taxes_total_ret > 0) {
                $implocal = [];
                $implocal['version'] = '1.0';
                $implocal['TotaldeRetenciones'] = Helper::numberFormat($local_taxes_total_ret, 2, false);
                $implocal['TotaldeTraslados'] = Helper::numberFormat($local_taxes_total, 2, false);
                $nodo_implocal = new \CfdiUtils\Nodes\Node(
                    'implocal:ImpuestosLocales', // nombre del elemento raíz
                    $implocal
                );
                if ($customer_lease->customerInvoiceTaxes->isNotEmpty()) {
                    foreach ($customer_lease->customerInvoiceTaxes as $key => $result) {
                        $tmp = $result->amount_tax;
                        $rate = $result->tax->rate;
                        if ($result->tax->factor == 'Tasa') {
                            $rate = $rate;
                        }
                        //Solo los impuestos locales
                        if ($result->tax->local_taxes) {
                            if($result->tax->type == 'R'){
                                $implocal_retenciones = [];
                                $implocal_retenciones['ImpLocRetenido'] = $result->tax->name;
                                $implocal_retenciones['TasadeRetencion'] = Helper::numberFormat(abs($rate), 2, false);
                                $implocal_retenciones['Importe'] = Helper::numberFormat(abs($tmp), 2, false);
                                $nodo_implocal_retenciones = new \CfdiUtils\Nodes\Node(
                                    'implocal:RetencionesLocales', // nombre del elemento raíz
                                    $implocal_retenciones
                                );
                                $nodo_implocal->addChild($nodo_implocal_retenciones);
                            }else {
                                $implocal_traslados = [];
                                $implocal_traslados['ImpLocTrasladado'] = $result->tax->name;
                                $implocal_traslados['TasadeTraslado'] = Helper::numberFormat(abs($rate), 2, false);
                                $implocal_traslados['Importe'] = Helper::numberFormat(abs($tmp), 2, false);
                                $nodo_implocal_traslados = new \CfdiUtils\Nodes\Node(
                                    'implocal:TrasladosLocales', // nombre del elemento raíz
                                    $implocal_traslados
                                );
                                $nodo_implocal->addChild($nodo_implocal_traslados);
                            }
                        }
                    }
                }
                //Agregamos al complemento
                $comprobante->addComplemento($nodo_implocal);
            }

            //Método de ayuda para establecer las sumas del comprobante e impuestos con base en la suma de los conceptos y la agrupación de sus impuestos
            //$creator->addSumasConceptos(null, 2);
            //Método de ayuda para generar el sello (obtener la cadena de origen y firmar con la llave privada)
            $creator->addSello('file://' . \Storage::path($customer_lease->company->pathFileKeyPassPem()), Crypt::decryptString($customer_lease->company->password_key));
            //Valida la estructura
            //$creator->validate();

            //Guarda XML
            //dd($creator->asXml());
            $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
            $file_xml = Helper::makeDirectoryCfdi($path_xml) . '/' . Str::random(40) . '.xml';
            $creator->saveXml(\Storage::path($path_xml . $file_xml));


            //Arreglo temporal para actualizar Customer Invoice CFDI
            $tmp = [
                'pac_id' => $pac->id,
                'cfdi_version' => setting('cfdi_version'),
                'uuid' => '',
                'date' => '',
                'path_xml' => $path_xml,
                'file_xml' => $file_xml,
                'file_xml_pac' => '',
                'pac' => $pac,
            ];

            //Timbrado de XML
            $class_pac = $pac->code;
            $tmp = PacHelper::$class_pac($tmp, $creator);

            return $tmp;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Crear XML y enviar a timbrar CFDI 4.0
     *
     * @param CustomerLease $customer_lease
     * @return array|\CfdiUtils\Elements\Cfdi33\Concepto|float|int
     * @throws \Exception
     */
    private function cfdi40(CustomerLease $customer_lease)
    {

        try {
            //Logica
            $pac = Pac::findOrFail(setting('default_pac_id')); //PAC

            //Arreglo CFDI 3.3
            $cfdi33 = [];
            if (!empty($customer_lease->serie)) {
                $cfdi33['Serie'] = $customer_lease->serie;
            }
            $cfdi33['Folio'] = $customer_lease->folio;
            $cfdi33['Fecha'] = \Date::parse($customer_lease->date)->format('Y-m-d\TH:i:s');
            //$cfdi33['Sello']
            $cfdi33['FormaPago'] = $customer_lease->paymentWay->code;
            $cfdi33['NoCertificado'] = $customer_lease->company->certificate_number;
            //$cfdi33['Certificado']
            $cfdi33['CondicionesDePago'] = $customer_lease->paymentTerm->name;
            $cfdi33['SubTotal'] = Helper::numberFormat($customer_lease->amount_untaxed + $customer_lease->amount_discount,
                $customer_lease->currency->decimal_place, false);
            if($customer_lease->amount_discount>0) {
                $cfdi33['Descuento'] = Helper::numberFormat($customer_lease->amount_discount,
                    $customer_lease->currency->decimal_place, false);
            }
            $cfdi33['Moneda'] = $customer_lease->currency->code;
            if ($customer_lease->currency->code != 'MXN') {
                $cfdi33['TipoCambio'] = Helper::numberFormat($customer_lease->currency_value, 4, false);
            }
            $cfdi33['Total'] = Helper::numberFormat($customer_lease->amount_total,
                $customer_lease->currency->decimal_place, false);
            $cfdi33['TipoDeComprobante'] = $customer_lease->documentType->cfdiType->code;
            $cfdi33['Exportacion'] = '01';
            $cfdi33['MetodoPago'] = $customer_lease->paymentMethod->code;
            $cfdi33['LugarExpedicion'] = $customer_lease->branchOffice->postcode;
            if (!empty($customer_lease->confirmacion)) {
                $cfdi33['Confirmacion'] = $customer_lease->confirmacion;
            }
            //---Informacion global
            $cfdi33_informacion_global = [];
            if ($customer_lease->customer->taxid == 'XAXX010101000' && $customer_lease->customer->name == 'PUBLICO EN GENERAL') {
                $cfdi33_informacion_global['Periodicidad'] = '01';
                $cfdi33_informacion_global['Meses'] = str_pad(date('m'), 2, '0', STR_PAD_LEFT);
                $cfdi33_informacion_global['Año'] = date('Y');
            }
            //---Cfdi Relacionados
            $cfdi33_relacionados = [];
            $cfdi33_relacionado = [];
            if (!empty($customer_lease->cfdi_relation_id)) {
                $cfdi33_relacionados['TipoRelacion'] = $customer_lease->cfdiRelation->code;
                if ($customer_lease->customerInvoiceRelations->isNotEmpty()) {
                    foreach ($customer_lease->customerInvoiceRelations as $key => $result) {
                        $cfdi33_relacionado[$key] = [];
                        $cfdi33_relacionado[$key]['UUID'] = $result->uuid_related;
                    }
                }
            }
            //---Emisor
            $cfdi33_emisor = [];
            $cfdi33_emisor['Rfc'] = $customer_lease->company->taxid;
            $cfdi33_emisor['Nombre'] = trim($customer_lease->company->name);
            $cfdi33_emisor['RegimenFiscal'] = !empty($customer_lease->tax_regimen_id) ? $customer_lease->taxRegimen->code : $customer_lease->company->taxRegimen->code;
            $cfdi33_emisor['FacAtrAdquirente'] = null;
            //---Receptor
            $cfdi33_receptor = [];
            $cfdi33_receptor['Rfc'] = $customer_lease->customer->taxid;
            $cfdi33_receptor['Nombre'] = trim($customer_lease->customer->name);
            if (!in_array($customer_lease->customer->taxid, ['XAXX010101000','XEXX010101000'])) {
                $cfdi33_receptor['DomicilioFiscalReceptor'] = $customer_lease->customer->postcode;
            }else{
                $cfdi33_receptor['DomicilioFiscalReceptor'] = $customer_lease->branchOffice->postcode;
            }
            if ($customer_lease->customer->taxid == 'XEXX010101000') {
                $cfdi33_receptor['ResidenciaFiscal'] = $customer_lease->customer->country->code;
                $cfdi33_receptor['NumRegIdTrib'] = $customer_lease->customer->numid;
            }
            $cfdi33_receptor['RegimenFiscalReceptor'] = !empty($customer_lease->tax_regimen_customer_id) ? $customer_lease->taxRegimenCustomer->code : $customer_lease->customer->taxRegimen->code;
            $cfdi33_receptor['UsoCFDI'] = $customer_lease->cfdiUse->code;
            //---Conceptos
            $cfdi33_conceptos = [];
            $cfdi33_conceptos_traslados = [];
            $cfdi33_conceptos_retenciones = [];
            $local_taxes_total = 0;
            $local_taxes_total_ret = 0;
            foreach ($customer_lease->customerActiveInvoiceLines as $key => $result) {
                $cfdi33_conceptos [$key]['ClaveProdServ'] = $result->satProduct->code;
                if (!empty($result->product->code)) {
                    $cfdi33_conceptos [$key]['NoIdentificacion'] = trim($result->product->code);
                }
                $cfdi33_conceptos [$key]['Cantidad'] = Helper::numberFormat($result->quantity, 6, false);
                $cfdi33_conceptos [$key]['ClaveUnidad'] = trim($result->unitMeasure->code);
                $cfdi33_conceptos [$key]['Unidad'] = str_limit(trim($result->unitMeasure->name),20,'');
                $cfdi33_conceptos [$key]['Descripcion'] = trim($result->name);
                $cfdi33_conceptos [$key]['ValorUnitario'] = Helper::numberFormatRealDecimalPlace($result->price_unit, false);
                $cfdi33_conceptos [$key]['Importe'] = Helper::numberFormat($result->amount_untaxed + $result->amount_discount,
                    2, false);
                if($result->amount_discount>0) {
                    $cfdi33_conceptos [$key]['Descuento'] = Helper::numberFormat($result->amount_discount, 2, false);
                }
                //['InformacionAduanera']
                //['CuentaPredial']
                if(!empty($result->customerInvoiceLineComplement)) {
                    if(!empty($result->customerInvoiceLineComplement->numero_predial)) {
                        $cfdi33_conceptos_compl_predial[$key] = [];
                        $cfdi33_conceptos_compl_predial[$key]['numero_predial'] = trim($result->customerInvoiceLineComplement->numero_predial);
                    }
                }
                //['ComplementoConcepto']
                //['Parte']
                //Complemento

                //Impuestos por concepto
                $cfdi33_conceptos_traslados[$key] = [];
                $cfdi33_conceptos_retenciones[$key] = [];
                if ($result->taxes) {
                    foreach ($result->taxes as $key2 => $result2) {
                        $tmp = 0;
                        $rate = $result2->rate;
                        if ($result2->factor == 'Tasa') {
                            $rate /= 100;
                            $tmp = $result->amount_untaxed * $rate;
                        } elseif ($result2->factor == 'Cuota') {
                            $tmp = $rate;
                        }
                        $tmp = round($tmp, 2);
                        if ($result2->type == 'R') { //Retenciones
                            if($result2->local_taxes) {
                                $local_taxes_total_ret += abs($tmp);
                                continue;
                            }
                            $cfdi33_conceptos_retenciones[$key][$key2] = [];
                            $cfdi33_conceptos_retenciones[$key][$key2]['Base'] = Helper::numberFormat($result->amount_untaxed,
                                2, false);
                            $cfdi33_conceptos_retenciones[$key][$key2]['Impuesto'] = $result2->code;
                            $cfdi33_conceptos_retenciones[$key][$key2]['TipoFactor'] = $result2->factor; //Para retenciones no hay exento
                            $cfdi33_conceptos_retenciones[$key][$key2]['TasaOCuota'] = Helper::numberFormat(abs($rate),
                                6, false);
                            $cfdi33_conceptos_retenciones[$key][$key2]['Importe'] = Helper::numberFormat(abs($tmp), 2,
                                false);
                        } else { //Traslados
                            if($result2->local_taxes) {
                                $local_taxes_total += abs($tmp);
                                continue;
                            }
                            $cfdi33_conceptos_traslados[$key][$key2] = [];
                            $cfdi33_conceptos_traslados[$key][$key2]['Base'] = Helper::numberFormat($result->amount_untaxed,
                                2, false);
                            $cfdi33_conceptos_traslados[$key][$key2]['Impuesto'] = $result2->code;
                            $cfdi33_conceptos_traslados[$key][$key2]['TipoFactor'] = $result2->factor; //Para retenciones no hay exento
                            if ($result2->factor != 'Exento') {
                                $cfdi33_conceptos_traslados[$key][$key2]['TasaOCuota'] = Helper::numberFormat(abs($rate),
                                    6, false);
                                $cfdi33_conceptos_traslados[$key][$key2]['Importe'] = Helper::numberFormat(abs($tmp), 2,
                                    false);
                            }
                        }
                    }
                }
                $cfdi33_conceptos [$key]['ObjetoImp'] = !empty($result->tax_object) ? $result->tax_object  : ((empty($cfdi33_conceptos_traslados[$key]) && empty($cfdi33_conceptos_retenciones[$key])) ? '01' : '02');
            }
            //Impuestos
            $cfdi33_retenciones = [];
            $cfdi33_traslados = [];
            $cfdi33_exento = null;
            if ($customer_lease->customerInvoiceTaxes->isNotEmpty()) {
                foreach ($customer_lease->customerInvoiceTaxes as $key => $result) {
                    $tmp = $result->amount_tax;
                    $rate = $result->tax->rate;
                    if ($result->tax->factor == 'Tasa') {
                        $rate /= 100;
                    }
                    //Omite los impuestos locales
                    if($result->tax->local_taxes) {
                        continue;
                    }
                    if ($result->tax->type == 'R') { //Retenciones
                        $cfdi33_retenciones[$key] = [];
                        $cfdi33_retenciones[$key]['Impuesto'] = $result->tax->code;
                        $cfdi33_retenciones[$key]['Importe'] = Helper::numberFormat(abs($tmp), $customer_lease->currency->decimal_place, false);
                    } else { //Traslados
                        $cfdi33_traslados[$key] = [];
                        $cfdi33_traslados[$key]['Base'] = Helper::numberFormat(abs($result->amount_base), 2, false);
                        $cfdi33_traslados[$key]['Impuesto'] = $result->tax->code;
                        $cfdi33_traslados[$key]['TipoFactor'] = $result->tax->factor;
                        if ($result->tax->factor != 'Exento') {
                            $cfdi33_traslados[$key]['TasaOCuota'] = Helper::numberFormat(abs($rate), 6, false);
                            $cfdi33_traslados[$key]['Importe'] = Helper::numberFormat(abs($tmp), 2, false);
                        }
                        if($result->tax->factor == 'Exento' && ($cfdi33_exento || is_null($cfdi33_exento)))
                        {
                            $cfdi33_exento = true;
                        }
                        if($result->tax->factor != 'Exento')
                        {
                            $cfdi33_exento = false;
                        }
                    }
                }
            }
            $cfdi33_impuestos = [];
            if (abs($customer_lease->amount_tax_ret) > 0 || !empty($cfdi33_retenciones)) {
                $tmp = Helper::numberFormat(abs($customer_lease->amount_tax_ret) - $local_taxes_total_ret, $customer_lease->currency->decimal_place, false);
                if($tmp > 0 || !empty($cfdi33_retenciones)){
                    $cfdi33_impuestos['TotalImpuestosRetenidos'] = $tmp;
                }
            }
            if (abs($customer_lease->amount_tax) > 0 || !empty($cfdi33_traslados)) {
                $cfdi33_impuestos['TotalImpuestosTrasladados'] = Helper::numberFormat(abs($customer_lease->amount_tax) - $local_taxes_total,
                    $customer_lease->currency->decimal_place, false);
            }
            if($cfdi33_exento){
                $cfdi33_impuestos['TotalImpuestosTrasladados'] = null;
            }

            //Genera XML
            $certificado = new \CfdiUtils\Certificado\Certificado(\Storage::path($customer_lease->company->pathFileCer()));
            $creator = new \CfdiUtils\CfdiCreator40($cfdi33, $certificado);
            $creator->setXmlResolver(PacHelper::resourcePathCfdiUtils()); //Almacenamiento local
            $comprobante = $creator->comprobante();

            $schema_location_implocal='';
            //Impuestos locales
            if($local_taxes_total > 0 || $local_taxes_total_ret > 0) {
                $schema_location_implocal=' http://www.sat.gob.mx/implocal http://www.sat.gob.mx/sitio_internet/cfd/implocal/implocal.xsd';
            }
            if(!empty($schema_location_implocal)){
                $comprobante->addAttributes([
                    'xsi:schemaLocation' => 'http://www.sat.gob.mx/cfd/4 http://www.sat.gob.mx/sitio_internet/cfd/4/cfdv40.xsd' . $schema_location_implocal,
                ]);
                if(!empty($schema_location_implocal)){
                    $comprobante->addAttributes([
                        'xmlns:implocal' => 'http://www.sat.gob.mx/implocal'
                    ]);
                }
            }

            if (!empty($cfdi33_informacion_global)) {
                $comprobante->addInformacionGlobal($cfdi33_informacion_global);
            }
            if (!empty($cfdi33_relacionados)) {
                $relacionados = $comprobante->addCfdiRelacionados($cfdi33_relacionados);
                if (!empty($cfdi33_relacionado)) {
                    foreach ($cfdi33_relacionado as $key => $result) {
                        $relacionados->addCfdiRelacionado($result);
                    }
                }
            }
            $comprobante->addEmisor($cfdi33_emisor);
            $comprobante->addReceptor($cfdi33_receptor);
            //Conceptos
            foreach ($cfdi33_conceptos as $key => $result) {
                $concepto = $comprobante->addConcepto($result);
                if (!empty($cfdi33_conceptos_traslados[$key])) {
                    foreach ($cfdi33_conceptos_traslados[$key] as $result2) {
                        $concepto->multiTraslado($result2);
                    }
                }
                if (!empty($cfdi33_conceptos_retenciones[$key])) {
                    foreach ($cfdi33_conceptos_retenciones[$key] as $result2) {
                        $concepto->multiRetencion($result2);
                    }
                }
                //Numero predial
                if(!empty($cfdi33_conceptos_compl_predial[$key]['numero_predial'])) {
                    $concepto->addCuentaPredial(['Numero' => $cfdi33_conceptos_compl_predial[$key]['numero_predial']]);
                }
            }
            //Impuestos
            if(!empty($cfdi33_impuestos)) {
                $comprobante->addImpuestos($cfdi33_impuestos);
                if (!empty($cfdi33_retenciones)) {
                    foreach ($cfdi33_retenciones as $result2) {
                        $comprobante->multiRetencion($result2);
                    }
                }
                if (!empty($cfdi33_traslados)) {
                    foreach ($cfdi33_traslados as $result2) {
                        $comprobante->multiTraslado($result2);
                    }
                }
            }

            //Complemento de impuestos locales
            if($local_taxes_total > 0 || $local_taxes_total_ret > 0) {
                $implocal = [];
                $implocal['version'] = '1.0';
                $implocal['TotaldeRetenciones'] = Helper::numberFormat($local_taxes_total_ret, 2, false);
                $implocal['TotaldeTraslados'] = Helper::numberFormat($local_taxes_total, 2, false);
                $nodo_implocal = new \CfdiUtils\Nodes\Node(
                    'implocal:ImpuestosLocales', // nombre del elemento raíz
                    $implocal
                );
                if ($customer_lease->customerInvoiceTaxes->isNotEmpty()) {
                    foreach ($customer_lease->customerInvoiceTaxes as $key => $result) {
                        $tmp = $result->amount_tax;
                        $rate = $result->tax->rate;
                        if ($result->tax->factor == 'Tasa') {
                            $rate = $rate;
                        }
                        //Solo los impuestos locales
                        if ($result->tax->local_taxes) {
                            if($result->tax->type == 'R'){
                                $implocal_retenciones = [];
                                $implocal_retenciones['ImpLocRetenido'] = $result->tax->name;
                                $implocal_retenciones['TasadeRetencion'] = Helper::numberFormat(abs($rate), 2, false);
                                $implocal_retenciones['Importe'] = Helper::numberFormat(abs($tmp), 2, false);
                                $nodo_implocal_retenciones = new \CfdiUtils\Nodes\Node(
                                    'implocal:RetencionesLocales', // nombre del elemento raíz
                                    $implocal_retenciones
                                );
                                $nodo_implocal->addChild($nodo_implocal_retenciones);
                            }else {
                                $implocal_traslados = [];
                                $implocal_traslados['ImpLocTrasladado'] = $result->tax->name;
                                $implocal_traslados['TasadeTraslado'] = Helper::numberFormat(abs($rate), 2, false);
                                $implocal_traslados['Importe'] = Helper::numberFormat(abs($tmp), 2, false);
                                $nodo_implocal_traslados = new \CfdiUtils\Nodes\Node(
                                    'implocal:TrasladosLocales', // nombre del elemento raíz
                                    $implocal_traslados
                                );
                                $nodo_implocal->addChild($nodo_implocal_traslados);
                            }
                        }
                    }
                }
                //Agregamos al complemento
                $comprobante->addComplemento($nodo_implocal);
            }

            //Método de ayuda para establecer las sumas del comprobante e impuestos con base en la suma de los conceptos y la agrupación de sus impuestos
            //$creator->addSumasConceptos(null, 2);
            //Método de ayuda para generar el sello (obtener la cadena de origen y firmar con la llave privada)
            $creator->addSello('file://' . \Storage::path($customer_lease->company->pathFileKeyPassPem()), Crypt::decryptString($customer_lease->company->password_key));
            //Valida la estructura
            //$creator->validate();

            //Guarda XML
            //dd($creator->asXml());
            $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
            $file_xml = Helper::makeDirectoryCfdi($path_xml) . '/' . Str::random(40) . '.xml';
            $creator->saveXml(\Storage::path($path_xml . $file_xml));


            //Arreglo temporal para actualizar Customer Invoice CFDI
            $tmp = [
                'pac_id' => $pac->id,
                'cfdi_version' => setting('cfdi_version'),
                'uuid' => '',
                'date' => '',
                'path_xml' => $path_xml,
                'file_xml' => $file_xml,
                'file_xml_pac' => '',
                'pac' => $pac,
            ];

            //Timbrado de XML
            $class_pac = $pac->code;
            $tmp = PacHelper::$class_pac($tmp, $creator);

            return $tmp;
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /**
     * Descarga de archivo XML
     *
     * @param Request $request
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Symfony\Component\HttpFoundation\BinaryFileResponse
     */
    public function downloadXml(Request $request, CustomerLease $customer_lease)
    {
        //Ruta y validacion del XML
        $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
        $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;
        if (!empty($customer_lease->customerInvoiceCfdi->file_xml_pac) && \Storage::exists($file_xml_pac)) {
            while (ob_get_level()) ob_end_clean();
            ob_start();

            return response()->download(\Storage::path($file_xml_pac), str_replace('/','',$customer_lease->name) . '.xml',['Cache-Control' => 'no-cache, must-revalidate']);
        }

        //Mensaje
        flash(__('sales/customer_lease.error_download_xml'))->error();

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Descarga de archivo PDF
     *
     * @param Request $request
     * @param CustomerInvoice $customer_invoice
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Symfony\Component\HttpFoundation\BinaryFileResponse
     */
    public function downloadPdf(Request $request, CustomerLease $customer_lease)
    {
        //Descarga archivo
        return $this->print($customer_lease, true);
    }

    /**
     * Cambiar estatus a abierta
     *
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function markOpen(CustomerLease $customer_lease)
    {
        //Logica
        if ((int)$customer_lease->status == CustomerLease::TO_PAY || (int)$customer_lease->status == CustomerLease::PAID) {
            $customer_lease->updated_uid = \Auth::user()->id;
            $customer_lease->status = CustomerLease::OPEN;
            $customer_lease->save();

            //Cancelacion del timbre fiscal

            //Mensaje
            flash(__('general.text_form_success_edit'))->success();
        } else {
            //Mensaje
            flash(__('sales/customer_lease.error_change_status'))->error();
        }

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Cambiar estatus a abierta
     *
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function markToPay(CustomerLease $customer_lease)
    {
        //Logica
        if ((int)$customer_lease->status == CustomerLease::OPEN || (int)$customer_lease->status == CustomerLease::PAID) {
            $customer_lease->updated_uid = \Auth::user()->id;
            $customer_lease->status = CustomerLease::TO_PAY;
            $customer_lease->save();

            //Cancelacion del timbre fiscal

            //Mensaje
            flash(__('general.text_form_success_edit'))->success();
        } else {
            //Mensaje
            flash(__('sales/customer_lease.error_change_status'))->error();
        }

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Cambiar estatus a enviada
     *
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function markSent(CustomerLease $customer_lease)
    {
        //Logica
        $customer_lease->updated_uid = \Auth::user()->id;
        $customer_lease->mail_sent = 1;
        $customer_lease->save();

        //Mensaje
        flash(__('general.text_form_success_edit'))->success();

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Cambiar estatus a pagada
     *
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function markPaid(CustomerLease $customer_lease)
    {

        //Logica
        if ((int)$customer_lease->status == CustomerLease::OPEN || (int)$customer_lease->status == CustomerLease::TO_PAY) {
            $customer_lease->updated_uid = \Auth::user()->id;
            $customer_lease->status = CustomerLease::PAID;
            $customer_lease->save();

            //Mensaje
            flash(__('general.text_form_success_edit'))->success();
        } else {
            //Mensaje
            flash(__('sales/customer_lease.error_change_status'))->error();
        }

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Clase generica de impresion
     *
     * @param CustomerLease $customer_lease
     * @return mixed
     */
    public function print(CustomerLease $customer_lease, $download = false, $save = false)
    {
        try {
            //Logica
            $tmp = 'default';
            if (!empty($customer_lease->customerInvoiceCfdi->cfdi_version)) {
                $tmp = $customer_lease->customerInvoiceCfdi->cfdi_version;
            }
            $class_print = 'print' . ucfirst($tmp);
            return $this->$class_print($customer_lease,$download,$save);

        } catch (\Exception $e) {
            flash($e->getMessage())->error();
            return redirect('/sales/customer-leases');
        }
    }

    /**
     * Impresion default
     *
     * @param $customer_lease
     * @return mixed
     */
    private function printDefault($customer_lease, $download = false, $save = false)
    {
        //PDF
        $pdf = \PDF::loadView('sales.customer_leases.pdf_default', compact('customer_lease'));

        //Guardar
        if ($save) {
            return $pdf->output();
        }

        //Redireccion
        if($download){
            return $pdf->download($customer_lease->documentType->name . '_' . str_replace('/','',$customer_lease->name) . '.pdf');
        }
        return $pdf->stream($customer_lease->documentType->name . '_' . str_replace('/','',$customer_lease->name) . '.pdf');
    }

    /**
     * Impresion CFDI 3.3
     *
     * @param $customer_lease
     * @param bool $download
     * @return mixed
     * @throws \Exception
     */
    private function printCfdi33($customer_lease, $download = false, $save = false)
    {
        //Datos del XML timbrado
        $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
        $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;
        $data = [];

        //Valida que el archivo exista
        if (!empty($customer_lease->customerInvoiceCfdi->file_xml_pac) && \Storage::exists($file_xml_pac)) {
            //Crea arreglo
            $cfdi = \CfdiUtils\Cfdi::newFromString(\Storage::get($file_xml_pac));
            $data = Cfdi33Helper::getQuickArrayCfdi($cfdi);

            //Genera codigo QR
            $image = QrCode::format('png')->size(150)->margin(0)->generate($data['qr_cadena']);
            $data['qr'] = 'data:image/png;base64,' . base64_encode($image);

            //Regimen fiscal
            $data['tax_regimen'] = TaxRegimen::where('code', $data['cfdi33']->emisor['RegimenFiscal'])->first()->name_sat;
        }

        //PDF
        $pdf_template = \App\Helpers\Helper::companyPdfTemplate($customer_lease->company_id);//Plantilla PDF
        $pdf = \PDF::loadView('sales.customer_leases.pdf_cfdi33_' . $pdf_template, compact('customer_lease', 'data'));

        //Guardar
        if ($save) {
            return $pdf->output();
        }

        //Redireccion
        if($download){
            return $pdf->download(str_replace('/','',$customer_lease->name) . '.pdf');
        }
        return $pdf->stream(str_replace('/','',$customer_lease->name) . '.pdf');
    }

    /**
     * Impresion CFDI 4.0
     *
     * @param $customer_lease
     * @param bool $download
     * @return mixed
     * @throws \Exception
     */
    private function printCfdi40($customer_lease, $download = false, $save = false)
    {
        //Datos del XML timbrado
        $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
        $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;
        $data = [];

        //Valida que el archivo exista
        if (!empty($customer_lease->customerInvoiceCfdi->file_xml_pac) && \Storage::exists($file_xml_pac)) {
            //Crea arreglo
            $cfdi = \CfdiUtils\Cfdi::newFromString(\Storage::get($file_xml_pac));
            $data = Cfdi33Helper::getQuickArrayCfdi($cfdi);

            //Genera codigo QR
            $image = QrCode::format('png')->size(150)->margin(0)->generate($data['qr_cadena']);
            $data['qr'] = 'data:image/png;base64,' . base64_encode($image);

            //Regimen fiscal
            $data['tax_regimen'] = TaxRegimen::where('code', $data['cfdi33']->emisor['RegimenFiscal'])->first()->name_sat;
            $data['tax_regimen_customer'] = TaxRegimen::where('code', $data['cfdi33']->receptor['RegimenFiscalReceptor'])->first()->name_sat;
        }

        //PDF
        $pdf_template = \App\Helpers\Helper::companyPdfTemplate($customer_lease->company_id);//Plantilla PDF
        $pdf = \PDF::loadView('sales.customer_leases.pdf_cfdi40_' . $pdf_template, compact('customer_lease', 'data'));

        //Guardar
        if ($save) {
            return $pdf->output();
        }

        //Redireccion
        if($download){
            return $pdf->download(str_replace('/','',$customer_lease->name) . '.pdf');
        }
        return $pdf->stream(str_replace('/','',$customer_lease->name) . '.pdf');
    }

    /**
     * Modal para envio de correo de factura
     *
     * @param Request $request
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    public function modalSendMail(Request $request, CustomerLease $customer_lease)
    {
        //Variables
        $id = $request->id;

        //Logica
        if ($request->ajax() && !empty($id)) {
            //Correo default del cliente
            $reply = Helper::replyMails();
            $to = [];
            $to_selected = [];
            if (!empty($customer_lease->customer->email)) {
                $tmp = explode(';',$customer_lease->customer->email);
                if(!empty($tmp)) {
                    foreach($tmp as $email) {
                        $email = trim($email);
                        $to[$email] = $email;
                        $to_selected [] = $email;
                    }
                }
            }
            //Etiquetas solo son demostrativas
            if($customer_lease->status != CustomerLease::DRAFT) {
                $files = [
                    'pdf' => str_replace('/','',$customer_lease->name) . '.pdf',
                    'xml' => str_replace('/','',$customer_lease->name) . '.xml'
                ];
            }else{
                $files = [
                    'pdf' => str_replace('/','',$customer_lease->name) . '.pdf',
                ];
            }
            $files_selected = array_keys($files);

            //modal de mensaje
            $html = view('layouts.partials.customer_leases.modal_send_mail',
                compact('customer_lease', 'to', 'to_selected', 'files', 'files_selected', 'reply'))->render();

            //Mensaje predefinido
            $custom_message = view('layouts.partials.customer_leases.message_send_mail',
                compact('customer_lease'))->render();

            return response()->json(['html' => $html, 'custom_message' => $custom_message]);
        }

        return response()->json(['error' => __('general.error_general')], 422);
    }

    /**
     * Envio de factura por correo
     *
     * @param Request $request
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function sendMail(Request $request, CustomerLease $customer_lease)
    {
        //Validaciones
        $validator = \Validator::make($request->all(), [
            'subject' => 'required',
            'to' => 'required',
            'to.*' => 'email',
            'message' => 'required'
        ], [
            'subject.*' => __('general.error_mail_subject'),
            'to.required' => __('general.error_mail_to'),
            'to.*.email' => __('general.error_mail_to_format'),
            'message.*' => __('general.error_mail_message'),
        ]);
        if ($validator->fails()) {
            $errors = '<ul>';
            foreach ($validator->errors()->all() as $message) {
                $errors .= '<li>'.$message . '</li>';
            }
            $errors .= '</ul>';
            return response()->json(['error' => $errors], 422);
        }

        //Creamos PDF en stream
        $pdf = $this->print($customer_lease);
        //Ruta del XML
        $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
        $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;

        //Envio de correo
        $to = $request->to;
        $reply = $request->reply;
        \Mail::to($to)->send(new SendCustomerLease($customer_lease, $request->subject, $request->message, $pdf,
            $file_xml_pac, $reply));

        //Actualiza campo de enviado
        $customer_lease->updated_uid = \Auth::user()->id;
        $customer_lease->mail_sent = 1;
        $customer_lease->save();

        //Mensaje
        return response()->json([
            'success' => sprintf(__('sales/customer_lease.text_success_send_mail'), $customer_lease->name)
        ]);
    }

    /**
     * Obtener registro
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function getCustomerLease(Request $request)
    {
        //Variables
        $id = $request->id;

        //Logica
        if ($request->ajax() && !empty($id)) {
            $customer_lease = CustomerLease::findOrFail($id);
            $customer_lease->uuid = $customer_lease->customerInvoiceCfdi->uuid ?? '';
            return response()->json($customer_lease, 200);
        }

        return response()->json(['error' => __('general.error_general')], 422);
    }

    /**
     * Autoacompletar select2 de facturas solo activas del SAT
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function autocompleteCfdi(Request $request)
    {
        //Variables
        $term = $request->term;
        $customer_id = !empty($request->customer_id) ? $request->customer_id : '111111111111'; //Filtra las facturas por cliente

        //Logica
        if ($request->ajax() && !empty($term)) {
            $tmp = CustomerLease::filter([
                'filter_search_cfdi_select2' => $term,
                'filter_customer_id' => $customer_id,
                'filter_document_type_code' => $this->document_type_code,
            ])->sortable('date')->limit(16)->get();
            $results = [];
            if ($tmp->isNotEmpty()) {
                foreach ($tmp as $result) {
                    $results[] = [
                        'id' => $result->id,
                        'text' => $result->text_select2,
                        'description' => $result->description_select2
                    ];
                }
            }
            return response()->json($results, 200);
        }

        return response()->json(['error' => __('general.error_general')], 422);
    }

    /**
     * Facturas con saldo
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    public function balances(Request $request)
    {
        //Variables
        $customer_id = $request->customer_id;
        $filter_currency_id = $request->filter_currency_id;
        //Logica
        if ($request->ajax() && !empty($customer_id)) {

            $currency = Currency::findOrFail($request->currency_id);
            $currency_value = $request->currency_value;
            $filter =[
                'filter_balances' => true,
                'filter_customer_id' => $customer_id,
                'filter_currency_id' => ((!empty($currency) && $currency->code != 'MXN') || !empty($filter_currency_id)) ? $currency->id : '', //Solo pagos en MXN se pueden aplicar a otras monedas
                'filter_document_type_code' => $this->document_type_code,
            ];

            //Consulta
            $results = CustomerLease::filter($filter)->sortable('date')->get();

            $html = view('layouts.partials.customer_leases.balances', compact('results','currency','filter_currency_id'))->render();

            return response()->json(['html' => $html]);
        }

        return response()->json(['error' => __('general.error_general')], 422);
    }

    /**
     * Calcula el total de las lineas
     *
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function totalLines(Request $request)
    {

        //Variables
        $json = new \stdClass;
        $input_items = $request->item;
        $currency_id = $request->currency_id;
        $currency_code = 'MXN';
        $taxes = [];

        if ($request->ajax()) {
            //Datos de moneda
            if (!empty($currency_id)) {
                $currency = Currency::findOrFail($currency_id);
                $currency_code = $currency->code;
            }
            //Variables de totales
            $amount_discount = 0;
            $amount_untaxed = 0;
            $amount_tax = 0;
            $amount_tax_ret = 0;
            $amount_total = 0;
            $balance = 0;
            $items = [];
            if (!empty($input_items)) {
                foreach ($input_items as $key => $item) {
                    //Logica
                    $item_quantity = (double)$item['quantity'];
                    $item_price_unit = (double)$item['price_unit'];
                    $item_discount = (double)$item['discount'];
                    $item_price_reduce = ($item_price_unit * (100 - $item_discount) / 100);
                    $item_amount_untaxed = round($item_quantity * $item_price_reduce, 2);
                    $item_amount_discount = round($item_quantity * $item_price_unit, 2) - $item_amount_untaxed;
                    $item_amount_tax = 0;
                    $item_amount_tax_ret = 0;
                    if (!empty($item['taxes'])) {
                        foreach ($item['taxes'] as $tax_id) {
                            if (!empty($tax_id)) {
                                $tax = Tax::findOrFail($tax_id);
                                $tmp = 0;
                                if ($tax->factor == 'Tasa') {
                                    $tmp = $item_amount_untaxed * $tax->rate / 100;
                                } elseif ($tax->factor == 'Cuota') {
                                    $tmp = $tax->rate;
                                }
                                $tmp = round($tmp, 2);
                                if ($tax->type == 'R') { //Retenciones
                                    $item_amount_tax_ret += $tmp;
                                } else { //Traslados
                                    $item_amount_tax += $tmp;
                                }

                                //Sumatoria de impuestos
                                $taxes[$tax_id] = array(
                                    'amount_base' => $item_amount_untaxed + (isset($taxes[$tax_id]['amount_base']) ? $taxes[$tax_id]['amount_base'] : 0),
                                    'amount_tax' => $tmp + (isset($taxes[$tax_id]['amount_tax']) ? $taxes[$tax_id]['amount_tax'] : 0),
                                );
                            }
                        }
                    }
                    $item_amount_total = $item_amount_untaxed + $item_amount_tax + $item_amount_tax_ret;
                    //Sumatoria totales
                    $amount_discount += $item_amount_discount;
                    $amount_untaxed += $item_amount_untaxed;
                    $amount_tax += $item_amount_tax;
                    $amount_tax_ret += $item_amount_tax_ret;
                    $amount_total += $item_amount_total;
                    //Subtotales por cada item
                    $items[$key] = money($item_amount_untaxed, $currency_code, true)->format();
                }
            }
            //Respuesta
            $taxes_tmp = [];
            if (!empty($taxes)) {
                foreach ($taxes as $tax_id => $result) {
                    $tax = Tax::findOrFail($tax_id);
                    $taxes_tmp[] = [
                        'name' => $tax->name,
                        'tax_id' => $tax_id,
                        'amount_base' => money($result['amount_base'], $currency_code, true)->format(),
                        'amount_tax' => money($result['amount_tax'], $currency_code, true)->format(),
                    ];
                }
            }

            //
            $json->items = $items;
            $json->taxes = $taxes_tmp;
            $json->amount_discount = money($amount_discount, $currency_code, true)->format();
            $json->amount_untaxed = money($amount_untaxed, $currency_code, true)->format();
            $json->amount_tax = money($amount_tax + $amount_tax_ret, $currency_code, true)->format();
            $json->amount_total = money($amount_total, $currency_code, true)->format();
            $json->amount_total_tmp = $amount_total;
            return response()->json($json);
        }

        return response()->json(['error' => __('general.error_general')], 422);
    }

    /**
     * Modal para cancelar factura
     *
     * @param Request $request
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    public function modalCancel(Request $request, CustomerLease $customer_lease)
    {
        //Variables
        $id = $request->id;

        //Logica
        if ($request->ajax() && !empty($id)) {
            //Obtener informacion de estatus
            $data_status_sat = [
                'cancelable' => 1,
                'pac_is_cancelable' => ''
            ];
            $reason_cancellations = ReasonCancellation::populateSelect()->get()->pluck('name_sat', 'id');
            if (!empty($customer_lease->customerInvoiceCfdi->cfdi_version) && !empty($customer_lease->customerInvoiceCfdi->uuid)) {

                //Obtener el sellos del CFDI
                $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
                $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;
                $cfdi = \CfdiUtils\Cfdi::newFromString(\Storage::get($file_xml_pac));
                $data = Cfdi33Helper::getQuickArrayCfdi($cfdi);

                $tmp = [
                    'rfcR' => $customer_lease->customer->taxid,
                    'uuid' => $customer_lease->customerInvoiceCfdi->uuid,
                    'total' => Helper::numberFormat($customer_lease->amount_total, $customer_lease->currency->decimal_place, false),
                    'fe' => substr($data['cfdi33']->complemento->timbreFiscalDigital['SelloCFD'],-8), //Los últimos 8 caracteres del Atributo Sello del CFDI
                    'file_xml_pac' => $file_xml_pac
                ];
                $class_pac = $customer_lease->customerInvoiceCfdi->pac->code . 'Status';
                $data_status_sat = PacHelper::$class_pac($tmp,$customer_lease->company,$customer_lease->customerInvoiceCfdi->pac);
            }
            $is_cancelable = true;
            if($data_status_sat['cancelable'] == 3){
                $is_cancelable = false;
            }

            //modal de cancelar
            $html = view('layouts.partials.customer_leases.modal_cancel', compact('customer_lease','data_status_sat','is_cancelable', 'reason_cancellations'))->render();

            return response()->json(['html' => $html]);
        }

        return response()->json(['error' => __('general.error_general')], 422);
    }

    /**
     * Exportar datos a excel
     *
     * @param Request $request
     * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
     */
    public function exportToExcel(Request $request)
    {
        while (ob_get_level()) ob_end_clean();
        ob_start();

        return Excel::download(new CustomerLeasesExport($request),
            __('sales/customer_lease.document_title') . '-' . config('app.name') . '.xlsx');
    }

    /**
     * Modal para historial de pagos y notas de credito aplicadas
     *
     * @param Request $request
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    public function modalPaymentHistory(Request $request, CustomerLease $customer_lease)
    {
        //Variables
        $id = $request->id;

        //Logica
        if ($request->ajax() && !empty($id)) {

            //modal de historial de pagos
            $html = view('layouts.partials.customer_leases.modal_payment_history',
                compact('customer_lease'))->render();


            return response()->json(['html' => $html]);
        }

        return response()->json(['error' => __('general.error_general')], 422);
    }

    /**
     * Funcion para timbrar factura con estatus de prefactura
     *
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function stamp(CustomerLease $customer_lease){

        \DB::connection('tenant')->beginTransaction();
        try {
            $invoiced = false;
            if((int) $customer_lease->status == CustomerLease::DRAFT) {
                //Valida que tenga exista la clase de facturacion
                $class_cfdi = setting('cfdi_version');
                if (empty($class_cfdi)) {
                    throw new \Exception(__('general.error_cfdi_version'));
                }
                if (!method_exists($this, $class_cfdi)) {
                    throw new \Exception(__('general.error_cfdi_class_exists'));
                }

                //Valida que tenga folios disponibles
                if(BaseHelper::getAvailableFolios()<=0){
                    throw new \Exception(__('general.error_available_folios'));
                }

                //CFDI
                $customer_lease_cfdi = $customer_lease->CustomerInvoiceCfdi;

                //Actualiza registr principal
                $customer_lease->updated_uid = \Auth::user()->id;
                $customer_lease->date = Helper::dateTimeToSql(BaseHelper::getDateTimeBranchOffice($customer_lease->branch_office_id));
                $customer_lease->status = CustomerLease::OPEN;
                $customer_lease->save();

                //Actualiza folios
                //Obtiene folio
                $document_type = Helper::getNextDocumentTypeByCode($this->document_type_code, $customer_lease->company->id, false, $customer_lease->branch_office_id);
                $customer_lease->draft = $customer_lease->name;
                $customer_lease->name = $document_type['name'];
                $customer_lease->serie = $document_type['serie'];
                $customer_lease->folio = $document_type['folio'];
                $customer_lease->save();
                $customer_lease_cfdi->name = $customer_lease->name;
                $customer_lease_cfdi->save();

                //Valida Empresa y PAC para timbrado
                PacHelper::validateSatActions($customer_lease->company);

                //Crear XML y timbra
                $tmp = $this->$class_cfdi($customer_lease);

                //Guardar registros de CFDI
                $customer_lease_cfdi->fill(array_only($tmp, [
                    'pac_id',
                    'cfdi_version',
                    'uuid',
                    'date',
                    'file_xml',
                    'file_xml_pac',
                ]));
                $customer_lease_cfdi->save();

                //Disminuye folios
                BaseHelper::decrementFolios();

                //Mensaje
                flash(__('general.text_success_customer_lease_cfdi'))->success();

                $invoiced = true;
            }

            \DB::connection('tenant')->commit();

            if($invoiced) {
                $this->saveCfdiDownloads($customer_lease, $customer_lease_cfdi);
            }


        } catch (\Exception $e) {
            \DB::connection('tenant')->rollback();
            flash( __('general.error_cfdi_pac').'<br>'.$e->getMessage())->error();
            return back()->withInput();
        }

        //Almacenamiento dropbox
        self::dropboxBackup($customer_lease);

        //Redireccion
        return redirect('/sales/customer-leases');
    }

    /**
     * Funcion para autorizar la cancelacion
     *
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function cancelAuthorized(CustomerLease $customer_lease){

        \DB::connection('tenant')->beginTransaction();
        try {

            if((int) $customer_lease->status == CustomerLease::CANCEL_PER_AUTHORIZED) {

                //Actualiza registro principal
                $customer_lease->updated_uid = \Auth::user()->id;
                $customer_lease->status = CustomerLease::CANCEL;
                $customer_lease->save();

                //Mensaje
                flash(__('general.text_form_success_cancel'))->success();
            }

            \DB::connection('tenant')->commit();

            //Redireccion
            return redirect('/sales/customer-leases');
        } catch (\Exception $e) {
            \DB::connection('tenant')->rollback();
            flash($e->getMessage())->error();
            return back()->withInput();
        }
    }

    /**
     * Funcion para cancelar la cancelacion
     *
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
     */
    public function cancelRejected(CustomerLease $customer_lease){

        \DB::connection('tenant')->beginTransaction();
        try {

            if((int) $customer_lease->status == CustomerLease::CANCEL_PER_AUTHORIZED) {

                //Actualiza registro principal
                $customer_lease->updated_uid = \Auth::user()->id;
                $customer_lease->status = CustomerLease::OPEN;
                $customer_lease->save();

                //Actualiza el cfdi
                $customer_lease->CustomerInvoiceCfdi->cancel_date = null;
                $customer_lease->CustomerInvoiceCfdi->cancel_response = 'Cancelación rechazada - ' . $customer_lease->CustomerInvoiceCfdi->cancel_response;
                //$customer_lease->CustomerInvoiceCfdi->cancel_state = null;
                $customer_lease->CustomerInvoiceCfdi->status = 1;
                $customer_lease->CustomerInvoiceCfdi->save();

                $cfdi_download = CfdiDownload::where('uuid', $customer_lease->customerInvoiceCfdi->uuid)->where('type', 2)->first();
                if(!empty($cfdi_download)){
                    $cfdi_download->status = 'Vigente';
                    $cfdi_download->save();
                }

                //Mensaje
                flash(__('general.text_success_customer_lease_rejected'))->success();
            }

            \DB::connection('tenant')->commit();

            //Redireccion
            return redirect('/sales/customer-leases');
        } catch (\Exception $e) {
            \DB::connection('tenant')->rollback();
            flash($e->getMessage())->error();
            return back()->withInput();
        }
    }

    /**
     * Modal para estatus de CFDI
     *
     * @param Request $request
     * @param CustomerLease $customer_lease
     * @return \Illuminate\Http\JsonResponse
     * @throws \Throwable
     */
    public function modalStatusSat(Request $request, CustomerLease $customer_lease)
    {
        //Variables
        $id = $request->id;

        //Logica
        if ($request->ajax() && !empty($id)) {
            //Obtener informacion de estatus
            $data_status_sat = [
                'cancelable' => 1,
                'pac_is_cancelable' => ''
            ];
            if (!empty($customer_lease->customerInvoiceCfdi->cfdi_version) && !empty($customer_lease->customerInvoiceCfdi->uuid)) {

                //Obtener el sellos del CFDI
                $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
                $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;
                $cfdi = \CfdiUtils\Cfdi::newFromString(\Storage::get($file_xml_pac));
                $data = Cfdi33Helper::getQuickArrayCfdi($cfdi);

                $tmp = [
                    'rfcR' => $customer_lease->customer->taxid,
                    'uuid' => $customer_lease->customerInvoiceCfdi->uuid,
                    'total' => Helper::numberFormat($customer_lease->amount_total, $customer_lease->currency->decimal_place, false),
                    'fe' => substr($data['cfdi33']->complemento->timbreFiscalDigital['SelloCFD'],-8), //Los últimos 8 caracteres del Atributo Sello del CFDI
                    'file_xml_pac' => $file_xml_pac
                ];
                $class_pac = $customer_lease->customerInvoiceCfdi->pac->code . 'Status';
                $data_status_sat = PacHelper::$class_pac($tmp,$customer_lease->company,$customer_lease->customerInvoiceCfdi->pac);
            }
            $is_cancelable = true;
            if($data_status_sat['cancelable'] == 3){
                $is_cancelable = false;
            }

            //modal de visualizar estatus en el SAT
            $html = view('layouts.partials.customer_leases.modal_status_sat', compact('customer_lease','data_status_sat','is_cancelable'))->render();

            return response()->json(['html' => $html]);
        }

        return response()->json(['error' => __('general.error_general')], 422);
    }

    /**
     * Respaldo en dropbox si esta activo
     *
     * @param $customer_lease
     */
    public function dropboxBackup($customer_lease, $save_xml = true, $save_pdf = true){
        try{
            if(!empty($customer_lease) && !empty(setting('dropbox_backup')) && !empty(setting('dropbox_access_token'))){
                $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
                $file_xml_pac = $path_xml . $customer_lease->customerInvoiceCfdi->file_xml_pac;
                if (!empty($customer_lease->customerInvoiceCfdi->file_xml_pac) && \Storage::exists($file_xml_pac)) {

                    $folder = !empty(setting('dropbox_folder','')) ? setting('dropbox_folder','') . '/' : setting('dropbox_folder','');
                    $folder .= strtoupper(\Date::parse($customer_lease->date)->format('F Y'));
                    $folder .= '/' .preg_replace("/[^a-zA-Z0-9\s]/", '', $customer_lease->company->name);

                    //Guarda XML
                    if($save_xml) {
                        \Storage::disk('dropbox')->putFileAs($folder,
                            new \Illuminate\Http\File(\Storage::path($file_xml_pac)),
                            str_replace('/','',$customer_lease->name) . '.xml');
                    }

                    //Guarda PDF
                    if($save_pdf) {
                        $pdf = $this->print($customer_lease, false, true);
                        \Storage::disk('dropbox')->put($folder . '/' . str_replace('/','',$customer_lease->name) . '.pdf', $pdf);
                    }
                }

            }
        }catch (\Exception $e){
            flash($e->getMessage())->error();
        }
    }

    private function saveCfdiDownloads($customer_lease, $customer_lease_cfdi){
        //Directorio de XML
        $path_files = Helper::setDirectory(CfdiDownload::PATH_FILES, $customer_lease->company->id);
        $tmp_path = str_replace(['files/', '/xml'], '', CustomerLease::PATH_XML_FILES_LEA);
        if (!\Storage::exists($path_files . '/' . $tmp_path)) {
            \Storage::makeDirectory($path_files . '/' . $tmp_path, 0777, true, true);
        }

        //Genera PDF
        $pdf = $this->print($customer_lease, false, true);
        $path_xml = Helper::setDirectory(CustomerLease::PATH_XML_FILES_LEA, $customer_lease->company_id) . '/';
        $file_xml = $tmp_path . '/' . $customer_lease_cfdi->uuid . '.xml';
        $file_pdf = str_replace('.xml', '.pdf', $file_xml);
        \Storage::put($path_files . '/' . $file_pdf, $pdf);

        if(!empty($customer_lease_cfdi->file_xml_pac)){
            \Storage::copy($path_xml . $customer_lease_cfdi->file_xml_pac, $path_files . '/' . $file_xml);
        }

        //Guarda registro en CFDIS descargados
        $cfdi_download = CfdiDownload::create([
            'created_uid' => \Auth::user()->id,
            'updated_uid' => \Auth::user()->id,
            'type' => 2,
            'uuid' => $customer_lease_cfdi->uuid,
            'file_xml' => $file_xml,
            'file_pdf' => $file_pdf,
            'file_acuse' => null,
            'status' => 'Vigente',
            'is_cancelable' => 'Cancelable sin aceptación',
            'date_cancel' => null,
            'company_id' => $customer_lease->company_id,
        ]);
        $data_xml = Helper::parseXmlToArrayCfdi33($path_xml . '/' . $customer_lease_cfdi->file_xml_pac);
        $data_xml['data']['customer_id'] = $customer_lease->customer_id;
        $data_xml['data']['caccounting_type_id'] = $customer_lease->customer->caccounting_type_sale_id ?? null;
        $cfdi_download->fill($data_xml['data']);
        $cfdi_download->save();
    }

}
