Files
WooBC/woo-business-central/admin/js/wbc-admin.js
Malin 0a08086272 perf: filter by BC category before fetching instead of per-product lookups
When wbc_category_filter is set, sync now pages through BC's /items
endpoint filtered server-side by itemCategoryCode (or-chained eq - BC's
classic OData rejects the `in` operator) and matches each returned item to
a WooCommerce product locally via wc_get_product_id_by_sku(), instead of
querying BC once per WooCommerce product only to discard most of them.
Confirmed against live data that BC's `in` filter isn't supported here but
chained `eq ... or ...` is.

Also reorders find_bc_item_by_sku() to try GTIN/number before Item
Reference (ReferenciasArticulo). Empirically, on this catalog Reference_No
is always identical to Item_No, so the reference lookup never matched
anything the GTIN/number checks didn't already find - it was costing an
extra BC round trip on every single product for no benefit. Kept as a
last-resort fallback both directions (SKU->item and item->SKU) in case
that doesn't hold for some future item.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 14:30:51 +02:00

315 lines
11 KiB
JavaScript

/**
* WooCommerce Business Central - Admin JavaScript
*/
(function($) {
'use strict';
var WBC_Admin = {
/**
* Initialize
*/
init: function() {
this.bindEvents();
this.checkInitialSyncStatus();
},
/**
* If a background sync is already running (e.g. triggered by cron,
* or the admin reloaded the page mid-sync), resume polling for it.
*/
checkInitialSyncStatus: function() {
var $btn = $('#wbc-manual-sync');
var $status = $('#wbc-sync-status');
if (!$btn.length) {
return;
}
$.ajax({
url: wbc_admin.ajax_url,
type: 'POST',
data: {
action: 'wbc_sync_status',
nonce: wbc_admin.nonce
},
success: function(response) {
if (response.success && response.data.status === 'running') {
$btn.prop('disabled', true);
$status.removeClass('success error').addClass('loading');
WBC_Admin.pollSyncStatus($btn, $status);
}
}
});
},
/**
* Bind event handlers
*/
bindEvents: function() {
// Test connection
$('#wbc-test-connection').on('click', this.testConnection);
// Manual sync
$('#wbc-manual-sync').on('click', this.manualSync);
// Clear logs
$('#wbc-clear-logs').on('click', this.clearLogs);
// Load companies
$('#wbc-load-companies').on('click', this.loadCompanies);
// Select company
$('#wbc-company-select').on('change', this.selectCompany);
// Toggle log data
$(document).on('click', '.wbc-toggle-data', this.toggleLogData);
},
/**
* Test connection to Business Central
*/
testConnection: function(e) {
e.preventDefault();
var $btn = $(this);
var $status = $('#wbc-connection-status');
$btn.prop('disabled', true);
$status.removeClass('success error').addClass('loading').text(wbc_admin.strings.testing);
$.ajax({
url: wbc_admin.ajax_url,
type: 'POST',
data: {
action: 'wbc_test_connection',
nonce: wbc_admin.nonce
},
success: function(response) {
$btn.prop('disabled', false);
$status.removeClass('loading');
if (response.success) {
$status.addClass('success').text(response.data.message);
} else {
$status.addClass('error').text(response.data.message || 'Connection failed');
}
},
error: function(xhr, status, error) {
$btn.prop('disabled', false);
$status.removeClass('loading').addClass('error').text('Request failed: ' + error);
}
});
},
/**
* Run manual sync
*
* The sync itself runs in the background (Action Scheduler batches),
* so this just kicks it off and then polls for progress instead of
* waiting on one long-lived request.
*/
manualSync: function(e) {
e.preventDefault();
var $btn = $(this);
var $status = $('#wbc-sync-status');
$btn.prop('disabled', true);
$status.removeClass('success error').addClass('loading').text(wbc_admin.strings.syncing);
$.ajax({
url: wbc_admin.ajax_url,
type: 'POST',
data: {
action: 'wbc_manual_sync',
nonce: wbc_admin.nonce
},
success: function(response) {
if (response.success) {
WBC_Admin.pollSyncStatus($btn, $status);
} else {
$btn.prop('disabled', false);
$status.removeClass('loading').addClass('error').text((response.data && response.data.message) || 'Sync failed');
}
},
error: function(xhr, status, error) {
$btn.prop('disabled', false);
$status.removeClass('loading').addClass('error').text('Request failed: ' + error);
}
});
},
/**
* Poll background sync progress until it completes
*/
pollSyncStatus: function($btn, $status) {
$.ajax({
url: wbc_admin.ajax_url,
type: 'POST',
data: {
action: 'wbc_sync_status',
nonce: wbc_admin.nonce
},
success: function(response) {
if (!response.success) {
$btn.prop('disabled', false);
$status.removeClass('loading').addClass('error').text((response.data && response.data.message) || 'Failed to get sync status');
return;
}
var run = response.data;
if (run.status === 'running') {
var progressText = (run.mode === 'category')
? (run.pages_done || 0) + ' page(s), ' + (run.total || 0) + ' item(s) checked'
: (run.batches_done || 0) + ' / ' + (run.total_batches || 0) + ' batches';
$status.text('Syncing... ' + progressText + ' (success: ' + (run.success || 0) + ', failed: ' + (run.failed || 0) + ', skipped: ' + (run.skipped || 0) + ')');
setTimeout(function() {
WBC_Admin.pollSyncStatus($btn, $status);
}, 3000);
} else if (run.status === 'completed') {
$btn.prop('disabled', false);
$status.removeClass('loading').addClass('success').text('Sync completed. Success: ' + (run.success || 0) + ', Failed: ' + (run.failed || 0) + ', Skipped: ' + (run.skipped || 0));
} else {
$btn.prop('disabled', false);
$status.removeClass('loading');
}
},
error: function(xhr, status, error) {
$btn.prop('disabled', false);
$status.removeClass('loading').addClass('error').text('Request failed: ' + error);
}
});
},
/**
* Clear all logs
*/
clearLogs: function(e) {
e.preventDefault();
if (!confirm(wbc_admin.strings.confirm_clear)) {
return;
}
var $btn = $(this);
var originalText = $btn.text();
$btn.prop('disabled', true).text(wbc_admin.strings.clearing);
$.ajax({
url: wbc_admin.ajax_url,
type: 'POST',
data: {
action: 'wbc_clear_logs',
nonce: wbc_admin.nonce
},
success: function(response) {
if (response.success) {
// Reload the page to show empty logs
location.reload();
} else {
alert(response.data.message || 'Failed to clear logs');
$btn.prop('disabled', false).text(originalText);
}
},
error: function(xhr, status, error) {
alert('Request failed: ' + error);
$btn.prop('disabled', false).text(originalText);
}
});
},
/**
* Load companies from Business Central
*/
loadCompanies: function(e) {
e.preventDefault();
var $btn = $(this);
var $select = $('#wbc-company-select');
var $list = $('#wbc-companies-list');
var originalText = $btn.text();
$btn.prop('disabled', true).text(wbc_admin.strings.loading);
$.ajax({
url: wbc_admin.ajax_url,
type: 'POST',
data: {
action: 'wbc_get_companies',
nonce: wbc_admin.nonce
},
success: function(response) {
$btn.prop('disabled', false).text(originalText);
if (response.success && response.data.companies) {
// Clear existing options except the first one
$select.find('option:not(:first)').remove();
// Add companies
$.each(response.data.companies, function(i, company) {
$select.append(
$('<option>', {
value: company.id,
text: company.displayName + ' (' + company.id.substring(0, 8) + '...)'
})
);
});
// Show the select
$list.slideDown();
// Pre-select current company if set
var currentCompany = $('#wbc_company_id').val();
if (currentCompany) {
$select.val(currentCompany);
}
} else {
alert(response.data.message || 'Failed to load companies. Make sure credentials are saved and correct.');
}
},
error: function(xhr, status, error) {
$btn.prop('disabled', false).text(originalText);
alert('Request failed: ' + error);
}
});
},
/**
* Select company from dropdown
*/
selectCompany: function() {
var companyId = $(this).val();
if (companyId) {
$('#wbc_company_id').val(companyId);
}
},
/**
* Toggle log data visibility
*/
toggleLogData: function(e) {
e.preventDefault();
var $btn = $(this);
var $data = $btn.siblings('.wbc-log-data');
$data.slideToggle(200, function() {
if ($data.is(':visible')) {
$btn.text($btn.data('hide-text') || 'Hide data');
} else {
$btn.text($btn.data('show-text') || 'Show data');
}
});
}
};
// Initialize on document ready
$(document).ready(function() {
WBC_Admin.init();
});
})(jQuery);