get_meta( '_woodoo_so_id' ) ) return; $api = woodoo_api(); if ( ! $api ) { $order->add_order_note( __( '[WooDoo] Odoo integration not configured – order not synced.', 'woodoo' ) ); return; } $partner_id = self::resolve_partner( $api, $order ); if ( ! $partner_id ) { $order->add_order_note( __( '[WooDoo] Could not find/create Odoo partner – order not synced.', 'woodoo' ) ); return; } $so_id = self::create_sales_order( $api, $order, $partner_id ); if ( $so_id ) { $order->update_meta_data( '_woodoo_so_id', $so_id ); $order->update_meta_data( '_woodoo_partner_id', $partner_id ); $order->save_meta_data(); $odoo_url = get_option( 'woodoo_odoo_url', '' ); $so_link = $odoo_url ? sprintf( '#%d', esc_url( $odoo_url ), $so_id, $so_id ) : "#$so_id"; /* translators: %s: Odoo SO link */ $order->add_order_note( sprintf( __( '[WooDoo] Sales Order created in Odoo: %s', 'woodoo' ), $so_link ) ); // If the WC customer has no partner_id set yet, save it $wc_user_id = $order->get_customer_id(); if ( $wc_user_id && ! get_user_meta( $wc_user_id, 'woodoo_odoo_partner_id', true ) ) { update_user_meta( $wc_user_id, 'woodoo_odoo_partner_id', $partner_id ); // Count synced orders $count = (int) get_user_meta( $wc_user_id, 'woodoo_so_count', true ); update_user_meta( $wc_user_id, 'woodoo_so_count', $count + 1 ); } else { $count = (int) get_user_meta( $wc_user_id, 'woodoo_so_count', true ); update_user_meta( $wc_user_id, 'woodoo_so_count', $count + 1 ); } } else { $order->add_order_note( __( '[WooDoo] Failed to create Odoo Sales Order. Check WooDoo logs.', 'woodoo' ) ); } } // ── Partner Resolution ──────────────────────────────────────────────── private static function resolve_partner( WooDoo_API $api, WC_Order $order ): ?int { // 1. Try from WC user meta first $wc_user_id = $order->get_customer_id(); if ( $wc_user_id ) { $partner_id = (int) get_user_meta( $wc_user_id, 'woodoo_odoo_partner_id', true ); if ( $partner_id > 0 ) return $partner_id; } // 2. Search Odoo by billing email $email = $order->get_billing_email(); if ( $email ) { $found = $api->search( 'res.partner', [ [ 'email', '=', $email ] ], 1 ); if ( ! empty( $found ) ) return (int) $found[0]; } // 3. Create new partner from order billing info $name = trim( $order->get_billing_first_name() . ' ' . $order->get_billing_last_name() ); if ( ! $name ) $name = $order->get_billing_company() ?: 'WooCommerce Customer'; $vals = [ 'name' => $name, 'email' => $email ?: false, 'phone' => $order->get_billing_phone() ?: false, 'street' => $order->get_billing_address_1() ?: false, 'street2' => $order->get_billing_address_2() ?: false, 'city' => $order->get_billing_city() ?: false, 'zip' => $order->get_billing_postcode() ?: false, 'is_company' => (bool) $order->get_billing_company(), ]; // Country $country_code = $order->get_billing_country(); if ( $country_code ) { $countries = $api->search_read( 'res.country', [ [ 'code', '=', $country_code ] ], [ 'id' ], 1 ); if ( ! empty( $countries ) ) { $vals['country_id'] = $countries[0]['id']; } } // Remove false values $vals = array_filter( $vals, fn( $v ) => $v !== false ); return $api->create( 'res.partner', $vals ); } // ── Sales Order Creation ────────────────────────────────────────────── private static function create_sales_order( WooDoo_API $api, WC_Order $order, int $partner_id ): ?int { $lines = self::build_order_lines( $api, $order ); $so_vals = [ 'partner_id' => $partner_id, 'client_order_ref' => 'WC-' . $order->get_order_number(), 'note' => $order->get_customer_note() ?: false, 'order_line' => $lines, ]; // If there's a WC-created date set it $date_created = $order->get_date_created(); if ( $date_created ) { $so_vals['date_order'] = $date_created->date( 'Y-m-d H:i:s' ); } // Remove false values $so_vals = array_filter( $so_vals, fn( $v ) => $v !== false ); return $api->create( 'sale.order', $so_vals ); } // ── Order Line Builder ──────────────────────────────────────────────── private static function build_order_lines( WooDoo_API $api, WC_Order $order ): array { $lines = []; foreach ( $order->get_items() as $item ) { /** @var WC_Order_Item_Product $item */ $sku = ''; $product = $item->get_product(); if ( $product ) $sku = $product->get_sku(); $odoo_product_id = $api->find_product_by_sku( $sku ); $line = [ 'name' => $item->get_name(), 'product_uom_qty' => (float) $item->get_quantity(), 'price_unit' => (float) $order->get_item_subtotal( $item, false, false ), ]; if ( $odoo_product_id ) { $line['product_id'] = $odoo_product_id; } // Command.create: [0, 0, vals] $lines[] = [ 0, 0, $line ]; } // Shipping line $shipping_total = (float) $order->get_shipping_total(); if ( $shipping_total > 0 ) { $lines[] = [ 0, 0, [ 'name' => sprintf( __( 'Shipping: %s', 'woodoo' ), $order->get_shipping_method() ), 'product_uom_qty' => 1, 'price_unit' => $shipping_total, ] ]; } // Discount line (if any coupon was applied) $discount = (float) $order->get_discount_total(); if ( $discount > 0 ) { $lines[] = [ 0, 0, [ 'name' => __( 'Discount', 'woodoo' ), 'product_uom_qty' => 1, 'price_unit' => -$discount, ] ]; } return $lines; } // ── Admin UI ────────────────────────────────────────────────────────── public static function show_so_meta( WC_Order $order ): void { $so_id = $order->get_meta( '_woodoo_so_id' ); $partner_id = $order->get_meta( '_woodoo_partner_id' ); if ( ! $so_id && ! $partner_id ) return; $odoo_url = get_option( 'woodoo_odoo_url', '' ); echo '
'; } public static function add_order_action( array $actions ): array { $actions['woodoo_resync'] = __( 'Re-sync to Odoo (WooDoo)', 'woodoo' ); return $actions; } public static function manual_resync( WC_Order $order ): void { // Clear previous SO ID so sync runs fresh $order->delete_meta_data( '_woodoo_so_id' ); $order->save_meta_data(); self::sync_order( $order->get_id(), $order ); } }