Manual and scheduled sync previously processed every WooCommerce product inline in a single PHP request/AJAX call, with several sequential BC API calls per product. At real catalog sizes this exceeds PHP's max_execution_time and the proxy's read timeout, surfacing as a 502 Bad Gateway on "Sync Now". Adds WBC_Batch_Sync, which queues the catalog in chunks of 25 via Action Scheduler (bundled with WooCommerce) and processes them asynchronously. The manual sync AJAX call now returns immediately after queuing, and the admin UI polls for progress instead of waiting on one long request. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
312 lines
11 KiB
JavaScript
312 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') {
|
|
$status.text('Syncing... ' + (run.batches_done || 0) + ' / ' + (run.total_batches || 0) + ' batches (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);
|