hola tengo este problema con este codigo
foreach ($order->get_items() as $item) {
$product = $item->get_product();
$qty = max(1, (int)$item->get_quantity());
$name = $item->get_name();
$desc = $product ? substr(wp_strip_all_tags($product->get_description()), 0, 200) : ‘’;
$split = $this->woo_alegra_lookup_split_for_name($name);
if (is_array($split) && isset($split['servicio_psicologa'], $split['costo_legatus'], $split['comision_plataforma'])) {
$alegra_item_id = $product ? (int)$product->get_meta('_alegra_item_id', true) : 0;
if ($alegra_item_id) {
$service_line = [
'id' => $alegra_item_id,
'price' => round((float)$split['servicio_psicologa'], 2),
'quantity' => $qty,
];
$tax_id = $this->map_tax_for_order_item($item);
if ($tax_id > 0) $service_line['tax'] = [['id' => (int)$tax_id]];
$items[] = $service_line;
}
$platform_mgmt = [
'id' => 127,
'price' => round((float)$split['costo_legatus'], 2),
'quantity' => $qty,
];
$platform_tax_id = apply_filters('woo_alegra_platform_fee_tax_id', 0, $item, $order);
if ((int)$platform_tax_id > 0) $platform_mgmt['tax'] = [['id' => (int)$platform_tax_id]];
$items[] = $platform_mgmt;
$platform_comm = [
'id' => 128,
'price' => round((float)$split['comision_plataforma'], 2),
'quantity' => $qty,
];
$commission_tax_id = apply_filters('woo_alegra_platform_commission_tax_id', 0, $item, $order);
if ((int)$commission_tax_id > 0) $platform_comm['tax'] = [['id' => (int)$commission_tax_id]];
$items[] = $platform_comm;
} else {
$unit_price = (float)$order->get_item_total($item, false, true);
$line = [
'name' => $name,
'description' => $desc,
'price' => round($unit_price, 2),
'quantity' => $qty,
];
$tax_id = $this->map_tax_for_order_item($item);
if ($tax_id > 0) $line['tax'] = [['id' => (int)$tax_id]];
$items[] = $line;
}
}
if ((float)$order->get_shipping_total() > 0) {
foreach ($order->get_items('shipping') as $ship) {
$line = [
'name' => 'Envío',
'description' => $ship->get_method_title(),
'price' => round((float)$order->get_shipping_total(), 2),
'quantity' => 1,
];
$tax_id = $this->map_tax_for_shipping_item($ship);
if ($tax_id > 0) $line['tax'] = [['id' => (int)$tax_id]];
$items[] = $line;
break;
}
}
$date_created = $order->get_date_created();
$date_str = $date_created ? $date_created->date('Y-m-d') : current_time('Y-m-d');
if ($test_mode) {
$company = trim((string)$order->get_billing_company());
$full = trim(trim((string)$order->get_billing_first_name()).' '.trim((string)$order->get_billing_last_name()));
$email = trim((string)$order->get_billing_email());
list($doc, $identType) = $this->woo_alegra_get_order_document_and_type($order);
$invoice_data = [
'client' => [
'id' => 'DRYRUN',
'name' => $company ?: ($full ?: ($email ?: ('Cliente Woo '.$order->get_id()))),
'email' => $email ?: null,
'identification' => $doc ?: null,
'identificationType' => $doc ? ($identType ?: 'CC') : null,
],
'date' => $date_str,
'dueDate' => $date_str,
'items' => $items,
'observations' => 'Pedido WooCommerce #'.$order->get_order_number(),
'status' => 'open',
];
if (!empty($opts['number_template_id'])) {
$invoice_data['numberTemplate'] = ['id'=>(string)$opts['number_template_id']];
}
$order->update_meta_data('_alegra_payload_dryrun', wp_json_encode($invoice_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
$order->save();
$order->add_order_note('Alegra (dry-run): payload generado y NO enviado.');
return;
}
try {
$api_client = $this->get_api_client();
$order->add_order_note('🔄 Paso 1: Creando/actualizando contacto en Alegra...');
$client_id = $api_client->create_or_update_client($order);
if (empty($client_id)) throw new Exception('No se pudo obtener un ID de cliente válido');
$order->add_order_note('✅ Paso 1: Cliente ID = ' . $client_id);
$order->add_order_note('🔄 Paso 2: Normalizando contacto (identificación/nombre/tipo)...');
try {
$this->ensure_contact_identification_ready($client_id, $order);
$order->add_order_note('✅ Paso 2: Contacto normalizado.');
} catch (\Throwable $t) {
$order->add_order_note('⚠️ Paso 2: No se pudo normalizar contacto: '.$t->getMessage());
}
$order->add_order_note('🔄 Paso 3: Leyendo datos actualizados del contacto...');
$contact = $api_client->get_contact($client_id);
$this->log('Contacto leído de Alegra: ' . wp_json_encode($contact, JSON_PRETTY_PRINT), 'info');
$order->update_meta_data('_alegra_contact_snapshot', wp_json_encode($contact, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
$order->save();
$order->add_order_note('✅ Paso 3: Contacto leído. Ver meta _alegra_contact_snapshot');
$order->add_order_note('🔄 Paso 4: Construyendo payload del cliente para factura...');
try {
$client_payload = $this->build_client_payload_for_invoice($client_id, $order, $contact);
list($doc_from_order, $doc_type_from_order) = $this->woo_alegra_get_order_document_and_type($order);
$safe_name = $this->woo_alegra_build_safe_name($order);
$ident_number = !empty($contact['identification']) ? (string)$contact['identification'] : (string)$doc_from_order;
$ident_type = !empty($contact['identificationObject']['type'])
? (string)$contact['identificationObject']['type']
: (!empty($contact['identificationType']) ? (string)$contact['identificationType'] : ($doc_type_from_order ?: 'CC'));
if (empty($client_payload) || !is_array($client_payload)) $client_payload = [];
if (empty($client_payload['id'])) $client_payload['id'] = (int)$client_id;
if (empty($client_payload['name'])) $client_payload['name'] = $safe_name;
if (empty($client_payload['identification']) && !empty($ident_number)) $client_payload['identification'] = $ident_number;
if (empty($client_payload['identificationType']) && !empty($ident_type)) $client_payload['identificationType'] = $ident_type;
$order->update_meta_data('_alegra_client_payload_built', wp_json_encode($client_payload, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
$order->save();
$order->add_order_note('✅ Paso 4: Client payload construido. Ver meta _alegra_client_payload_built');
} catch (Exception $e) {
$order->add_order_note('❌ Paso 4 FALLÓ: ' . $e->getMessage());
throw $e;
}
$paymentForm = $order->is_paid() ? 'CASH' : 'CREDIT';
$gateway_id = method_exists($order, 'get_payment_method') ? (string) $order->get_payment_method() : '';
$gateway_id_lc = strtolower($gateway_id);
$paymentMethod = 'CASH';
switch (true) {
case strpos($gateway_id_lc, 'wompi') !== false:
case strpos($gateway_id_lc, 'payu') !== false:
case strpos($gateway_id_lc, 'mercado') !== false:
case strpos($gateway_id_lc, 'stripe') !== false:
case strpos($gateway_id_lc, 'redsis') !== false:
case strpos($gateway_id_lc, 'epayco') !== false:
case strpos($gateway_id_lc, 'placetopay') !== false:
case strpos($gateway_id_lc, 'woocommerce-payments') !== false:
$paymentMethod = 'CREDIT_CARD';
$paymentForm = 'CASH';
break;
case strpos($gateway_id_lc, 'transfer') !== false:
case strpos($gateway_id_lc, 'banc') !== false:
case strpos($gateway_id_lc, 'pse') !== false:
$paymentMethod = 'TRANSFER';
$paymentForm = 'CASH';
break;
case strpos($gateway_id_lc, 'contraentrega') !== false:
case strpos($gateway_id_lc, 'cod') !== false:
case strpos($gateway_id_lc, 'cash') !== false:
$paymentMethod = 'CASH';
$paymentForm = 'CASH';
break;
default:
if (!$order->is_paid()) { $paymentForm = 'CREDIT'; $paymentMethod = 'CASH'; }
else { $paymentForm = 'CASH'; $paymentMethod = 'CASH'; }
}
$paymentForm = apply_filters('woo_alegra_payment_form', $paymentForm, $order, $gateway_id);
$paymentMethod = apply_filters('woo_alegra_payment_method', $paymentMethod, $order, $gateway_id);
$order->add_order_note('🧾 Forma/medio de pago Alegra → paymentForm='.$paymentForm.' | paymentMethod='.$paymentMethod.' | gateway='.$gateway_id);
$order->add_order_note('🔄 Paso 5: Construyendo payload completo de factura...');
foreach ($items as &$it) { if (empty($it['unit'])) $it['unit'] = 'service'; }
unset($it);
$items = $this->ensure_alegra_items_active($items, $api_client, $order);
$client_address = [];
if (!empty($contact['address']) && is_array($contact['address'])) {
$client_address = array_filter([
'address' => $contact['address']['address'] ?? null,
'city' => $contact['address']['city'] ?? null,
'department' => $contact['address']['department'] ?? null,
'country' => $contact['address']['country'] ?? null,
'zipCode' => $contact['address']['zipCode'] ?? null,
], fn($v)=>$v!==null && $v!=='');
}
if (empty($client_address['city']) || empty($client_address['department']) || empty($client_address['country'])) {
$defs = apply_filters('woo_alegra_default_address', [
'country' => 'Colombia',
'department' => 'Bogotá D.C.',
'city' => 'Bogotá',
]);
$client_address = array_merge(['address'=>'Calle'], $defs, $client_address);
}
$client_payload['address'] = $client_address;
$due_date_str = $date_str;
if ($paymentForm === 'CREDIT') {
$dt = date_create_from_format('Y-m-d', $date_str) ?: new DateTime($date_str);
$dt->modify('+15 days');
$due_date_str = $dt->format('Y-m-d');
}
$is_electronic_series = isset($opts['number_template_id_is_electronic'])
? (bool)$opts['number_template_id_is_electronic']
: true;
$invoice_data = [
'client' => $client_payload,
'date' => $date_str,
'dueDate' => $due_date_str,
'items' => $items,
'observations' => 'Pedido WooCommerce #'.$order->get_order_number(),
'status' => 'open',
'paymentForm' => $paymentForm,
'paymentMethod' => $paymentMethod,
];
if (!empty($opts['ubl21_enabled'])) {
$invoice_data['operationType'] = 'STANDARD';
}
if (!empty($opts['number_template_id'])) {
$invoice_data['numberTemplate'] = ['id' => (string)$opts['number_template_id']];
}
$invoice_data = apply_filters('woo_alegra_invoice_payload', $invoice_data, $order);
list($doc_from_order2, $doc_type_from_order2) = $this->woo_alegra_get_order_document_and_type($order);
$safe_name2 = $this->woo_alegra_build_safe_name($order);
$ident_number2 = isset($client_payload['identification']) ? (string)$client_payload['identification'] : (string)$doc_from_order2;
$ident_type2 = isset($client_payload['identificationType']) ? (string)$client_payload['identificationType'] : ($doc_type_from_order2 ?: 'CC');
$required_client = [
'id' => (int) $client_id,
'name' => $safe_name2,
'identification' => $ident_number2,
'identificationType' => $ident_type2,
];
if (!isset($invoice_data['client']) || is_int($invoice_data['client'])) {
$invoice_data['client'] = $required_client;
} else {
$cli = (array) $invoice_data['client'];
foreach ($required_client as $k => $v) {
$is_missing = !array_key_exists($k, $cli);
$is_empty = !$is_missing && ($cli[$k] === null || $cli[$k] === '');
if ($is_missing || $is_empty) $cli[$k] = $v;
}
$cli['id'] = (int)$cli['id'];
$invoice_data['client'] = $cli;
}
$order->update_meta_data('_alegra_client_after_filters', wp_json_encode($invoice_data['client'], JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
$order->update_meta_data('_alegra_invoice_payload_final', wp_json_encode($invoice_data, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
$order->save();
$order->add_order_note('✅ Paso 5: Payload completo. Ver meta _alegra_invoice_payload_final');
$order->add_order_note('🔄 Paso 6: Enviando factura a Alegra API...');
$this->log('=== PAYLOAD FINAL DE FACTURA ===: ' . wp_json_encode($invoice_data, JSON_PRETTY_PRINT), 'info');
$order->add_order_note('📋 Payload factura (client): ' . wp_json_encode($invoice_data['client'], JSON_PRETTY_PRINT));
$response = $api_client->create_invoice($invoice_data);
if (!$response || !isset($response['id'])) {
throw new Exception('Respuesta de Alegra sin ID de factura');
}
$order->update_meta_data('_alegra_invoice_id', $response['id']);
if (!empty($response['numberTemplate']['number'])) {
$order->update_meta_data('_alegra_invoice_number', $response['numberTemplate']['number']);
$order->add_order_note('✅ Factura Alegra creada: #'.$response['numberTemplate']['number']);
} else {
$order->add_order_note('✅ Factura Alegra creada (ID '.$response['id'].')');
}
$order->save();
$this->log('Factura creada exitosamente para pedido #'.$order_id.': '.$response['id']);
try {
$inv_snapshot = $api_client->request('invoices/'.(int)$response['id'], 'GET');
if ($inv_snapshot) {
$order->update_meta_data('_alegra_invoice_snapshot', wp_json_encode($inv_snapshot, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
$order->save();
}
} catch (\Throwable $e) {
$this->log('No se pudo obtener snapshot de factura: '.$e->getMessage(), 'warning');
}
try {
$currency = get_woocommerce_currency();
$total_paid = (float) $order->get_total();
$collection_payload = [
'date' => $date_str,
'client' => ['id' => (int) $client_id],
'paymentForm' => $paymentForm,
'paymentMethod' => $paymentMethod,
'currency' => ['code' => $currency],
'observations' => 'Cobro automático WooCommerce del pedido #'.$order->get_order_number(),
'amount' => $total_paid,
'invoices' => [
['id' => (int) $response['id'], 'amount' => $total_paid],
],
];
$collection_payload = apply_filters('woo_alegra_collection_payload', $collection_payload, $order, $response);
$order->update_meta_data('_alegra_collection_payload', wp_json_encode($collection_payload, JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));
$order->save();
$this->log('Registrando cobro Alegra: '.wp_json_encode($collection_payload), 'info');
$col_resp = $api_client->create_collection($collection_payload);
if (!empty($col_resp['id'])) {
$order->update_meta_data('_alegra_collection_id', $col_resp['id']);
$order->add_order_note('💰 Cobro registrado en Alegra (collection ID '.$col_resp['id'].'). Factura debe quedar "Cobrada".');
$order->save();
} else {
$order->add_order_note('⚠️ Cobro no retornó ID. Verifica en Alegra.');
}
} catch (\Exception $e) {
$msg = $e->getMessage();
if (strpos($msg, '403') !== false || stripos($msg, 'forbidden') !== false) {
$order->add_order_note('⚠️ Alegra devolvió 403 al registrar cobro (Collections). '
.'Puede ser falta de permisos del plan/tenant o del token API. '
.'La factura quedó creada, pero NO “cobrada”.');
$this->log('Collections 403: '.$msg, 'error');
} else {
$this->log('Error registrando cobro: '.$msg, 'error');
$order->add_order_note('⚠️ No se pudo registrar el cobro: '.$msg);
}
}
$series_is_electronic = $is_electronic_series;
if (isset($response['numberTemplate']['isElectronic'])) {
$series_is_electronic = (bool)$response['numberTemplate']['isElectronic'];
} elseif (isset($response['isElectronic'])) {
$series_is_electronic = (bool)$response['isElectronic'];
}
$should_emit = apply_filters('woo_alegra_emit_to_dian', true, $order, $response);
if ($should_emit && $series_is_electronic) {
$invoice_id = (int) $response['id'];
$emit_ok = false; $emit_errs = [];
try {
$this->log('Intentando emisión DIAN vía /invoices/'.$invoice_id.'/e-invoice', 'info');
$emit_resp = $api_client->request('invoices/'.$invoice_id.'/e-invoice', 'POST');
$emit_ok = !empty($emit_resp);
} catch (\Throwable $e) { $emit_errs[] = 'e-invoice: '.$e->getMessage(); }
if (!$emit_ok) {
try {
$this->log('Intentando emisión DIAN vía /e-invoicing/invoices/'.$invoice_id.'/send', 'info');
$emit_resp = $api_client->request('e-invoicing/invoices/'.$invoice_id.'/send', 'POST');
$emit_ok = !empty($emit_resp);
} catch (\Throwable $e) { $emit_errs[] = 'e-invoicing: '.$e->getMessage(); }
}
if ($emit_ok) {
$order->add_order_note('📤 Envío electrónico a DIAN solicitado.');
$order->save();
} else {
$order->add_order_note('⚠️ No se pudo invocar la emisión a DIAN (revisa endpoints/plan). Intentos: '.implode(' | ', $emit_errs));
}
} else {
$order->add_order_note('↩️ Emisión DIAN omitida (serie no electrónica o filtro la desactivó).');
}
} catch (Exception $e) {
$error_msg = $e->getMessage();
$this->log('Error al crear factura para pedido #'.$order_id.': '.$error_msg, 'error');
$order->add_order_note('❌ ERROR: ' . $error_msg);
return;
}
}