Merge branch 'master' into feature/product-custom-tables-supporting-changes
This commit is contained in:
commit
b6e73f528b
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1347,11 +1347,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.wc-order-item-bulk-edit .cancel-action {
|
||||
float: left;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.add_meta {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
@ -1450,19 +1445,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
td {
|
||||
cursor: pointer;
|
||||
}
|
||||
&.selected {
|
||||
background: #f5ebf3;
|
||||
td {
|
||||
border-color: #e6cce1;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom: 1px solid #dfdfdf;
|
||||
}
|
||||
|
@ -5894,20 +5876,58 @@
|
|||
padding: 1.5em;
|
||||
|
||||
p {
|
||||
margin: 1.5em 0;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
p:first-child {
|
||||
margin-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.pagination {
|
||||
padding: 10px 0 0;
|
||||
text-align: center;
|
||||
padding: 10px 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
table.widefat {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
|
||||
thead th {
|
||||
padding: 0 1em 1em 1em;
|
||||
text-align: left;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
tbody td, tbody th {
|
||||
padding: 1em;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
|
||||
&:first-child {
|
||||
padding-left: 0;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
select,
|
||||
.select2-container {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
position: absolute;
|
||||
|
@ -6311,11 +6331,13 @@
|
|||
border: 0;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
|
||||
td, th {
|
||||
border: 0;
|
||||
padding: 12px;
|
||||
vertical-align: middle;
|
||||
word-wrap: break-word;
|
||||
|
||||
select {
|
||||
width: 100%;
|
||||
|
|
|
@ -244,11 +244,6 @@ jQuery( function ( $ ) {
|
|||
.on( 'click', 'button.calculate-action', this.recalculate )
|
||||
.on( 'click', 'a.edit-order-item', this.edit_item )
|
||||
.on( 'click', 'a.delete-order-item', this.delete_item )
|
||||
.on( 'click', 'tr.item, tr.fee, tr.shipping, tr.refund', this.select_row )
|
||||
.on( 'click', 'tr.item :input, tr.fee :input, tr.shipping :input, tr.refund :input, tr.item a, tr.fee a, tr.shipping a, tr.refund a', this.select_row_child )
|
||||
.on( 'click', 'button.bulk-delete-items', this.bulk_actions.do_delete )
|
||||
.on( 'click', 'button.bulk-increase-stock', this.bulk_actions.do_increase_stock )
|
||||
.on( 'click', 'button.bulk-decrease-stock', this.bulk_actions.do_reduce_stock )
|
||||
|
||||
// Refunds
|
||||
.on( 'click', '.delete_refund', this.refunds.delete_refund )
|
||||
|
@ -900,194 +895,24 @@ jQuery( function ( $ ) {
|
|||
}
|
||||
},
|
||||
|
||||
select_row: function() {
|
||||
var $row = false;
|
||||
if ( $( this ).is( 'tr' ) ) {
|
||||
$row = $( this );
|
||||
} else {
|
||||
$row = $( this ).closest( 'tr' );
|
||||
}
|
||||
var $table = $( this ).closest( 'table' );
|
||||
|
||||
if ( $row.is( '.selected' ) ) {
|
||||
$row.removeClass( 'selected' );
|
||||
} else {
|
||||
$row.addClass( 'selected' );
|
||||
}
|
||||
|
||||
var $rows = $table.find( 'tr.selected' );
|
||||
var $bulk_edit_wraper = $( 'div.wc-order-item-bulk-edit' );
|
||||
|
||||
if ( $rows.length && $bulk_edit_wraper.children().length > 0 ) {
|
||||
$bulk_edit_wraper.slideDown();
|
||||
|
||||
var selected_product = false;
|
||||
|
||||
$rows.each( function() {
|
||||
if ( $( this ).is( 'tr.item' ) ) {
|
||||
selected_product = true;
|
||||
}
|
||||
} );
|
||||
|
||||
if ( selected_product ) {
|
||||
$( '.bulk-increase-stock, .bulk-decrease-stock' ).show();
|
||||
} else {
|
||||
$( '.bulk-increase-stock, .bulk-decrease-stock' ).hide();
|
||||
}
|
||||
} else {
|
||||
$( 'div.wc-order-item-bulk-edit' ).slideUp();
|
||||
}
|
||||
},
|
||||
|
||||
select_row_child: function( e ) {
|
||||
e.stopPropagation();
|
||||
},
|
||||
|
||||
bulk_actions: {
|
||||
|
||||
do_delete: function( e ) {
|
||||
e.preventDefault();
|
||||
var $table = $( 'table.woocommerce_order_items' );
|
||||
var $rows = $table.find( 'tr.selected' );
|
||||
|
||||
if ( $rows.length && window.confirm( woocommerce_admin_meta_boxes.remove_item_notice ) ) {
|
||||
|
||||
wc_meta_boxes_order_items.block();
|
||||
|
||||
var delete_items = [];
|
||||
var delete_refunds = [];
|
||||
var deferred = [];
|
||||
|
||||
$.map( $rows, function( row ) {
|
||||
var $row = $( row );
|
||||
|
||||
if ( $row.is( '.refund' ) ) {
|
||||
delete_refunds.push( parseInt( $( $row ).data( 'order_refund_id' ), 10 ) );
|
||||
} else {
|
||||
delete_items.push( parseInt( $( $row ).data( 'order_item_id' ), 10 ) );
|
||||
}
|
||||
return ;
|
||||
});
|
||||
|
||||
if ( delete_items.length ) {
|
||||
deferred.push( $.ajax({
|
||||
url : woocommerce_admin_meta_boxes.ajax_url,
|
||||
data: {
|
||||
order_id : woocommerce_admin_meta_boxes.post_id,
|
||||
order_item_ids: delete_items,
|
||||
action: 'woocommerce_remove_order_item',
|
||||
security: woocommerce_admin_meta_boxes.order_item_nonce
|
||||
},
|
||||
type: 'POST'
|
||||
} ) );
|
||||
}
|
||||
|
||||
if ( delete_refunds.length ) {
|
||||
deferred.push( $.ajax( {
|
||||
url : woocommerce_admin_meta_boxes.ajax_url,
|
||||
data: {
|
||||
action: 'woocommerce_delete_refund',
|
||||
refund_id: delete_refunds,
|
||||
security: woocommerce_admin_meta_boxes.order_item_nonce
|
||||
},
|
||||
type: 'POST'
|
||||
} ) );
|
||||
}
|
||||
|
||||
if ( deferred ) {
|
||||
$.when.apply( $, deferred ).done( function() {
|
||||
wc_meta_boxes_order_items.reload_items();
|
||||
wc_meta_boxes_order_items.unblock();
|
||||
} );
|
||||
} else {
|
||||
wc_meta_boxes_order_items.unblock();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
modify_stock: function( e, action ) {
|
||||
e.preventDefault();
|
||||
wc_meta_boxes_order_items.block();
|
||||
|
||||
$( '#woocommerce-order-notes' ).block({
|
||||
message: null,
|
||||
overlayCSS: {
|
||||
background: '#fff',
|
||||
opacity: 0.6
|
||||
}
|
||||
});
|
||||
|
||||
var $table = $( 'table.woocommerce_order_items' );
|
||||
var $rows = $table.find( 'tr.selected' );
|
||||
var quantities = {};
|
||||
var item_ids = $.map( $rows, function( $row ) {
|
||||
return parseInt( $( $row ).data( 'order_item_id' ), 10 );
|
||||
});
|
||||
|
||||
$rows.each(function() {
|
||||
if ( $( this ).find( 'input.quantity' ).length ) {
|
||||
quantities[ $( this ).attr( 'data-order_item_id' ) ] = $( this ).find( 'input.quantity' ).val();
|
||||
}
|
||||
});
|
||||
|
||||
var data = {
|
||||
order_id: woocommerce_admin_meta_boxes.post_id,
|
||||
order_item_ids: item_ids,
|
||||
order_item_qty: quantities,
|
||||
action: action,
|
||||
security: woocommerce_admin_meta_boxes.order_item_nonce
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: woocommerce_admin_meta_boxes.ajax_url,
|
||||
data: data,
|
||||
type: 'POST',
|
||||
success: function( response ) {
|
||||
wc_meta_boxes_order_items.unblock();
|
||||
|
||||
if ( true === response.success ) {
|
||||
$.map( response.data, function( item ) {
|
||||
|
||||
// No items were updated.
|
||||
if ( ! item.success ) {
|
||||
window.alert( item.note );
|
||||
return;
|
||||
}
|
||||
|
||||
var order_note_data = {
|
||||
action: 'woocommerce_add_order_note',
|
||||
post_id: woocommerce_admin_meta_boxes.post_id,
|
||||
note: item.note,
|
||||
note_type: '',
|
||||
security: woocommerce_admin_meta_boxes.add_order_note_nonce
|
||||
};
|
||||
|
||||
$.post( woocommerce_admin_meta_boxes.ajax_url, order_note_data, function( response ) {
|
||||
$( 'ul.order_notes' ).prepend( response );
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$( '#woocommerce-order-notes' ).unblock();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
do_increase_stock: function( e ) {
|
||||
wc_meta_boxes_order_items.bulk_actions.modify_stock( e, 'woocommerce_increase_order_item_stock' );
|
||||
},
|
||||
|
||||
do_reduce_stock: function( e ) {
|
||||
wc_meta_boxes_order_items.bulk_actions.modify_stock( e, 'woocommerce_reduce_order_item_stock' );
|
||||
}
|
||||
},
|
||||
|
||||
backbone: {
|
||||
|
||||
init: function( e, target ) {
|
||||
if ( 'wc-modal-add-products' === target ) {
|
||||
$( document.body ).trigger( 'wc-enhanced-select-init' );
|
||||
$( '#add_item_id' ).selectWoo( 'open' ).selectWoo( 'focus' );
|
||||
|
||||
$( this ).on( 'change', '.wc-product-search', function() {
|
||||
if ( ! $( this ).closest( 'tr' ).is( ':last-child' ) ) {
|
||||
return;
|
||||
}
|
||||
var item_table = $( this ).closest( 'table.widefat' ),
|
||||
item_table_body = item_table.find( 'tbody' ),
|
||||
index = item_table_body.find( 'tr' ).length,
|
||||
row = item_table_body.data( 'row' ).replace( /\[0\]/g, '[' + index + ']' );
|
||||
|
||||
item_table_body.append( '<tr>' + row + '</tr>' );
|
||||
$( document.body ).trigger( 'wc-enhanced-select-init' );
|
||||
} );
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1103,39 +928,58 @@ jQuery( function ( $ ) {
|
|||
wc_meta_boxes_order_items.backbone.add_tax( rate_id, manual_rate_id );
|
||||
}
|
||||
if ( 'wc-modal-add-products' === target ) {
|
||||
wc_meta_boxes_order_items.backbone.add_item( data.add_order_items );
|
||||
// Build array of data.
|
||||
var item_table = $( this ).find( 'table.widefat' ),
|
||||
item_table_body = item_table.find( 'tbody' ),
|
||||
rows = item_table_body.find( 'tr' ),
|
||||
add_items = [];
|
||||
|
||||
$( rows ).each( function() {
|
||||
var item_id = $( this ).find( ':input[name="item_id"]' ).val(),
|
||||
item_qty = $( this ).find( ':input[name="item_qty"]' ).val();
|
||||
|
||||
add_items.push( {
|
||||
'id' : item_id,
|
||||
'qty': item_qty ? item_qty: 1
|
||||
} );
|
||||
} );
|
||||
|
||||
return wc_meta_boxes_order_items.backbone.add_items( add_items );
|
||||
}
|
||||
},
|
||||
|
||||
add_item: function( add_item_ids ) {
|
||||
if ( add_item_ids ) {
|
||||
wc_meta_boxes_order_items.block();
|
||||
add_items: function( add_items ) {
|
||||
wc_meta_boxes_order_items.block();
|
||||
|
||||
var data = {
|
||||
action : 'woocommerce_add_order_item',
|
||||
item_to_add: add_item_ids,
|
||||
dataType : 'json',
|
||||
order_id : woocommerce_admin_meta_boxes.post_id,
|
||||
security : woocommerce_admin_meta_boxes.order_item_nonce,
|
||||
data : $( '#wc-backbone-modal-dialog form' ).serialize()
|
||||
};
|
||||
var data = {
|
||||
action : 'woocommerce_add_order_item',
|
||||
order_id : woocommerce_admin_meta_boxes.post_id,
|
||||
security : woocommerce_admin_meta_boxes.order_item_nonce,
|
||||
data : add_items
|
||||
};
|
||||
|
||||
// Check if items have changed, if so pass them through so we can save them before adding a new item.
|
||||
if ( 'true' === $( 'button.cancel-action' ).attr( 'data-reload' ) ) {
|
||||
data.items = $( 'table.woocommerce_order_items :input[name], .wc-order-totals-items :input[name]' ).serialize();
|
||||
}
|
||||
// Check if items have changed, if so pass them through so we can save them before adding a new item.
|
||||
if ( 'true' === $( 'button.cancel-action' ).attr( 'data-reload' ) ) {
|
||||
data.items = $( 'table.woocommerce_order_items :input[name], .wc-order-totals-items :input[name]' ).serialize();
|
||||
}
|
||||
|
||||
$.post( woocommerce_admin_meta_boxes.ajax_url, data, function( response ) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: woocommerce_admin_meta_boxes.ajax_url,
|
||||
data: data,
|
||||
success: function( response ) {
|
||||
if ( response.success ) {
|
||||
$( '#woocommerce-order-items' ).find( '.inside' ).empty();
|
||||
$( '#woocommerce-order-items' ).find( '.inside' ).append( response.data.html );
|
||||
wc_meta_boxes_order_items.reloaded_items();
|
||||
wc_meta_boxes_order_items.unblock();
|
||||
} else {
|
||||
wc_meta_boxes_order_items.unblock();
|
||||
window.alert( response.data.error );
|
||||
}
|
||||
wc_meta_boxes_order_items.unblock();
|
||||
});
|
||||
}
|
||||
},
|
||||
dataType: 'json'
|
||||
});
|
||||
},
|
||||
|
||||
add_tax: function( rate_id, manual_rate_id ) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -87,12 +87,13 @@ jQuery( function( $ ) {
|
|||
delay: 250,
|
||||
data: function( params ) {
|
||||
return {
|
||||
term: params.term,
|
||||
action: $( this ).data( 'action' ) || 'woocommerce_json_search_products_and_variations',
|
||||
security: wc_enhanced_select_params.search_products_nonce,
|
||||
exclude: $( this ).data( 'exclude' ),
|
||||
include: $( this ).data( 'include' ),
|
||||
limit: $( this ).data( 'limit' )
|
||||
term : params.term,
|
||||
action : $( this ).data( 'action' ) || 'woocommerce_json_search_products_and_variations',
|
||||
security : wc_enhanced_select_params.search_products_nonce,
|
||||
exclude : $( this ).data( 'exclude' ),
|
||||
include : $( this ).data( 'include' ),
|
||||
limit : $( this ).data( 'limit' ),
|
||||
display_stock: $( this ).data( 'display_stock' )
|
||||
};
|
||||
},
|
||||
processResults: function( data ) {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -138,8 +138,9 @@
|
|||
var $tr = view.$el.find( 'tr[data-id="' + rowData.instance_id + '"]');
|
||||
|
||||
if ( ! rowData.has_settings ) {
|
||||
$tr.find( '.wc-shipping-zone-method-title a').replaceWith( $tr.find( '.wc-shipping-zone-method-title' ).text() );
|
||||
$tr.find( '.wc-shipping-zone-method-settings' ).remove();
|
||||
$tr.find( '.wc-shipping-zone-method-title > a' ).replaceWith('<span>' + $tr.find( '.wc-shipping-zone-method-title > a' ).text() + '</span>' );
|
||||
var $del = $tr.find( '.wc-shipping-zone-method-delete' );
|
||||
$tr.find( '.wc-shipping-zone-method-title .row-actions' ).empty().html($del);
|
||||
}
|
||||
} );
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -89,7 +89,9 @@ jQuery( function( $ ) {
|
|||
get_payment_method: function() {
|
||||
return wc_checkout_form.$checkout_form.find( 'input[name="payment_method"]:checked' ).val();
|
||||
},
|
||||
payment_method_selected: function() {
|
||||
payment_method_selected: function( e ) {
|
||||
e.stopPropagation();
|
||||
|
||||
if ( $( '.payment_methods input.input-radio' ).length > 1 ) {
|
||||
var target_payment_box = $( 'div.payment_box.' + $( this ).attr( 'ID' ) ),
|
||||
is_checked = $( this ).is( ':checked' );
|
||||
|
|
|
@ -1,86 +1,115 @@
|
|||
( function( $ ) {
|
||||
$( function() {
|
||||
var wcTokenizationForm = (function() {
|
||||
function wcTokenizationForm( target ) {
|
||||
var $target = $( target ),
|
||||
$formWrap = $target.closest( '.payment_box' ),
|
||||
$wcTokenizationForm = this;
|
||||
/*global wc_tokenization_form_params */
|
||||
jQuery( function( $ ) {
|
||||
|
||||
this.onTokenChange = function() {
|
||||
if ( 'new' === $( this ).val() ) {
|
||||
$wcTokenizationForm.showForm();
|
||||
$wcTokenizationForm.showSaveNewCheckbox();
|
||||
} else {
|
||||
$wcTokenizationForm.hideForm();
|
||||
$wcTokenizationForm.hideSaveNewCheckbox();
|
||||
}
|
||||
};
|
||||
/**
|
||||
* WCTokenizationForm class.
|
||||
*/
|
||||
var TokenizationForm = function( $target ) {
|
||||
this.$target = $target;
|
||||
this.$formWrap = $target.closest( '.payment_box' );
|
||||
|
||||
this.onCreateAccountChange = function() {
|
||||
if ( $( this ).is( ':checked' ) ) {
|
||||
$wcTokenizationForm.showSaveNewCheckbox();
|
||||
} else {
|
||||
$wcTokenizationForm.hideSaveNewCheckbox();
|
||||
}
|
||||
};
|
||||
// Params.
|
||||
this.params = $.extend( {}, {
|
||||
'is_registration_required': false,
|
||||
'is_logged_in' : false,
|
||||
}, wc_tokenization_form_params );
|
||||
|
||||
this.onDisplay = function() {
|
||||
// Make sure a radio button is selected if there is no is_default for this payment method..
|
||||
if ( 0 === $( ':input.woocommerce-SavedPaymentMethods-tokenInput:checked', $target ).length ) {
|
||||
$( ':input.woocommerce-SavedPaymentMethods-tokenInput:last', $target ).prop( 'checked', true );
|
||||
}
|
||||
// Bind functions to this.
|
||||
this.onDisplay = this.onDisplay.bind( this );
|
||||
this.hideForm = this.hideForm.bind( this );
|
||||
this.showForm = this.showForm.bind( this );
|
||||
this.showSaveNewCheckbox = this.showSaveNewCheckbox.bind( this );
|
||||
this.hideSaveNewCheckbox = this.hideSaveNewCheckbox.bind( this );
|
||||
|
||||
// Don't show the "use new" radio button if we only have one method..
|
||||
if ( 0 === $target.data( 'count' ) ) {
|
||||
$( '.woocommerce-SavedPaymentMethods-new', $target ).hide();
|
||||
}
|
||||
// When a radio button is changed, make sure to show/hide our new CC info area.
|
||||
this.$target.on( 'click change', ':input.woocommerce-SavedPaymentMethods-tokenInput', { tokenizationForm: this }, this.onTokenChange );
|
||||
|
||||
// Trigger change event
|
||||
$( ':input.woocommerce-SavedPaymentMethods-tokenInput:checked', $target ).trigger( 'change' );
|
||||
// OR if create account is checked.
|
||||
$( 'input#createaccount' ).change( { tokenizationForm: this }, this.onCreateAccountChange );
|
||||
|
||||
// Hide "save card" if "Create Account" is not checked.
|
||||
// Check that the field is shown in the form - some plugins and force create account remove it
|
||||
if ( $( 'input#createaccount' ).length && ! $('input#createaccount').is( ':checked' ) ) {
|
||||
$wcTokenizationForm.hideSaveNewCheckbox();
|
||||
}
|
||||
// First display.
|
||||
this.onDisplay();
|
||||
};
|
||||
|
||||
};
|
||||
TokenizationForm.prototype.onDisplay = function() {
|
||||
// Make sure a radio button is selected if there is no is_default for this payment method..
|
||||
if ( 0 === $( ':input.woocommerce-SavedPaymentMethods-tokenInput:checked', this.$target ).length ) {
|
||||
$( ':input.woocommerce-SavedPaymentMethods-tokenInput:last', this.$target ).prop( 'checked', true );
|
||||
}
|
||||
|
||||
this.hideForm = function() {
|
||||
$( '.wc-payment-form', $formWrap ).hide();
|
||||
};
|
||||
// Don't show the "use new" radio button if we only have one method..
|
||||
if ( 0 === this.$target.data( 'count' ) ) {
|
||||
$( '.woocommerce-SavedPaymentMethods-new', this.$target ).remove();
|
||||
}
|
||||
|
||||
this.showForm = function() {
|
||||
$( '.wc-payment-form', $formWrap ).show();
|
||||
};
|
||||
// Hide "save card" if "Create Account" is not checked and registration is not forced.
|
||||
var hasCreateAccountCheckbox = 0 < $( 'input#createaccount' ).length,
|
||||
createAccount = hasCreateAccountCheckbox && $( 'input#createaccount' ).is( ':checked' );
|
||||
|
||||
this.showSaveNewCheckbox = function() {
|
||||
$( '.woocommerce-SavedPaymentMethods-saveNew', $formWrap ).show();
|
||||
};
|
||||
if ( createAccount || this.params.is_logged_in || this.params.is_registration_required ) {
|
||||
this.showSaveNewCheckbox();
|
||||
} else {
|
||||
this.hideSaveNewCheckbox();
|
||||
}
|
||||
|
||||
this.hideSaveNewCheckbox = function() {
|
||||
$( '.woocommerce-SavedPaymentMethods-saveNew', $formWrap ).hide();
|
||||
};
|
||||
// Trigger change event
|
||||
$( ':input.woocommerce-SavedPaymentMethods-tokenInput:checked', this.$target ).trigger( 'change' );
|
||||
};
|
||||
|
||||
// When a radio button is changed, make sure to show/hide our new CC info area
|
||||
$( ':input.woocommerce-SavedPaymentMethods-tokenInput', $target ).change( this.onTokenChange );
|
||||
TokenizationForm.prototype.onTokenChange = function( event ) {
|
||||
if ( 'new' === $( this ).val() ) {
|
||||
event.data.tokenizationForm.showForm();
|
||||
event.data.tokenizationForm.showSaveNewCheckbox();
|
||||
} else {
|
||||
event.data.tokenizationForm.hideForm();
|
||||
event.data.tokenizationForm.hideSaveNewCheckbox();
|
||||
}
|
||||
};
|
||||
|
||||
// OR if create account is checked
|
||||
$ ( 'input#createaccount' ).change( this.onCreateAccountChange );
|
||||
TokenizationForm.prototype.onCreateAccountChange = function( event ) {
|
||||
if ( $( this ).is( ':checked' ) ) {
|
||||
event.data.tokenizationForm.showSaveNewCheckbox();
|
||||
} else {
|
||||
event.data.tokenizationForm.hideSaveNewCheckbox();
|
||||
}
|
||||
};
|
||||
|
||||
this.onDisplay();
|
||||
}
|
||||
TokenizationForm.prototype.hideForm = function() {
|
||||
$( '.wc-payment-form', this.$formWrap ).hide();
|
||||
};
|
||||
|
||||
return wcTokenizationForm;
|
||||
})();
|
||||
TokenizationForm.prototype.showForm = function() {
|
||||
$( '.wc-payment-form', this.$formWrap ).show();
|
||||
};
|
||||
|
||||
$( document.body ).on( 'updated_checkout wc-credit-card-form-init', function() {
|
||||
// Loop over gateways with saved payment methods
|
||||
var $saved_payment_methods = $( 'ul.woocommerce-SavedPaymentMethods' );
|
||||
TokenizationForm.prototype.showSaveNewCheckbox = function() {
|
||||
$( '.woocommerce-SavedPaymentMethods-saveNew', this.$formWrap ).show();
|
||||
};
|
||||
|
||||
$saved_payment_methods.each( function() {
|
||||
new wcTokenizationForm( this );
|
||||
} );
|
||||
TokenizationForm.prototype.hideSaveNewCheckbox = function() {
|
||||
$( '.woocommerce-SavedPaymentMethods-saveNew', this.$formWrap ).hide();
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to call wc_product_gallery on jquery selector.
|
||||
*/
|
||||
$.fn.wc_tokenization_form = function( args ) {
|
||||
new TokenizationForm( this, args );
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize.
|
||||
*/
|
||||
$( document.body ).on( 'updated_checkout wc-credit-card-form-init', function() {
|
||||
// Loop over gateways with saved payment methods
|
||||
var $saved_payment_methods = $( 'ul.woocommerce-SavedPaymentMethods' );
|
||||
|
||||
$saved_payment_methods.each( function() {
|
||||
$( this ).wc_tokenization_form();
|
||||
} );
|
||||
});
|
||||
})( jQuery );
|
||||
} );
|
||||
|
||||
// Alias.
|
||||
var wcTokenizationForm = TokenizationForm;
|
||||
} );
|
||||
|
|
|
@ -48,7 +48,7 @@ jQuery( function( $ ) {
|
|||
return false;
|
||||
}
|
||||
} )
|
||||
.on( 'focus', function() {
|
||||
.on( 'click focus', function() {
|
||||
var input = $( this ),
|
||||
parent = input.parent(),
|
||||
description = parent.find( 'span.description' );
|
||||
|
|
|
@ -1372,7 +1372,13 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
foreach ( $this->get_shipping_methods() as $item_id => $item ) {
|
||||
$taxes = $item->get_taxes();
|
||||
foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
|
||||
$shipping_taxes[ $tax_rate_id ] = isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] + (float) $tax : (float) $tax;
|
||||
$tax_amount = (float) $tax;
|
||||
|
||||
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
|
||||
$tax_amount = wc_round_tax_total( $tax_amount );
|
||||
}
|
||||
|
||||
$shipping_taxes[ $tax_rate_id ] = isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] + $tax_amount : $tax_amount;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1429,13 +1435,13 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
|
||||
// Sum line item costs.
|
||||
foreach ( $this->get_items() as $item ) {
|
||||
$cart_subtotal += $item->get_subtotal();
|
||||
$cart_total += $item->get_total();
|
||||
$cart_subtotal += round( $item->get_subtotal(), wc_get_price_decimals() );
|
||||
$cart_total += round( $item->get_total(), wc_get_price_decimals() );
|
||||
}
|
||||
|
||||
// Sum shipping costs.
|
||||
foreach ( $this->get_shipping_methods() as $shipping ) {
|
||||
$shipping_total += $shipping->get_total();
|
||||
$shipping_total += round( $shipping->get_total(), wc_get_price_decimals() );
|
||||
}
|
||||
|
||||
$this->set_shipping_total( $shipping_total );
|
||||
|
@ -1703,7 +1709,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
// Show shipping excluding tax.
|
||||
$shipping = wc_price( $this->get_shipping_total(), array( 'currency' => $this->get_currency() ) );
|
||||
|
||||
if ( $this->get_shipping_tax() !== 0 && $this->get_prices_include_tax() ) {
|
||||
if ( (float) $this->get_shipping_tax() > 0 && $this->get_prices_include_tax() ) {
|
||||
$shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>', $this, $tax_display );
|
||||
}
|
||||
} else {
|
||||
|
@ -1711,7 +1717,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
// Show shipping including tax.
|
||||
$shipping = wc_price( $this->get_shipping_total() + $this->get_shipping_tax(), array( 'currency' => $this->get_currency() ) );
|
||||
|
||||
if ( $this->get_shipping_tax() !== 0 && ! $this->get_prices_include_tax() ) {
|
||||
if ( (float) $this->get_shipping_tax() > 0 && ! $this->get_prices_include_tax() ) {
|
||||
$shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', ' <small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>', $this, $tax_display );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -441,6 +441,13 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
|
|||
array( 'jquery' ),
|
||||
WC()->version
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'woocommerce-tokenization-form', 'wc_tokenization_form_params', array(
|
||||
'is_registration_required' => WC()->checkout()->is_registration_required(),
|
||||
'is_logged_in' => is_user_logged_in(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -902,7 +902,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
|
|||
$class = 'standard' === $class ? '' : $class;
|
||||
$valid_classes = $this->get_valid_tax_classes();
|
||||
|
||||
if ( ! in_array( $class, $valid_classes ) ) {
|
||||
if ( ! in_array( $class, $valid_classes, true ) ) {
|
||||
$class = '';
|
||||
}
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@ abstract class WC_Widget extends WP_Widget {
|
|||
$instance[ $key ] = empty( $new_instance[ $key ] ) ? 0 : 1;
|
||||
break;
|
||||
default:
|
||||
$instance[ $key ] = sanitize_text_field( $new_instance[ $key ] );
|
||||
$instance[ $key ] = isset( $new_instance[ $key ] ) ? sanitize_text_field( $new_instance[ $key ] ) : $setting['std'];
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -269,18 +269,23 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
|
|||
wp_enqueue_script( 'wc-admin-coupon-meta-boxes', WC()->plugin_url() . '/assets/js/admin/meta-boxes-coupon' . $suffix . '.js', array( 'wc-admin-meta-boxes' ), WC_VERSION );
|
||||
}
|
||||
if ( in_array( str_replace( 'edit-', '', $screen_id ), array_merge( array( 'shop_coupon', 'product' ), wc_get_order_types( 'order-meta-boxes' ) ) ) ) {
|
||||
$post_id = isset( $post->ID ) ? $post->ID : '';
|
||||
$currency = '';
|
||||
$post_id = isset( $post->ID ) ? $post->ID : '';
|
||||
$currency = '';
|
||||
$remove_item_notice = __( 'Are you sure you want to remove the selected items?', 'woocommerce' );
|
||||
|
||||
if ( $post_id && in_array( get_post_type( $post_id ), wc_get_order_types( 'order-meta-boxes' ) ) ) {
|
||||
$order = wc_get_order( $post_id );
|
||||
if ( $order ) {
|
||||
$currency = $order->get_currency();
|
||||
$currency = $order->get_currency();
|
||||
|
||||
if ( ! $order->has_status( array( 'pending', 'failed', 'cancelled' ) ) ) {
|
||||
$remove_item_notice = $remove_item_notice . ' ' . __( "You may need to manually restore the item's stock.", 'woocommerce' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$params = array(
|
||||
'remove_item_notice' => __( "Are you sure you want to remove the selected items? If you have previously reduced this item's stock, or this order was submitted by a customer, you will need to manually restore the item's stock.", 'woocommerce' ),
|
||||
'remove_item_notice' => $remove_item_notice,
|
||||
'i18n_select_items' => __( 'Please select some items.', 'woocommerce' ),
|
||||
'i18n_do_refund' => __( 'Are you sure you wish to process this refund? This action cannot be undone.', 'woocommerce' ),
|
||||
'i18n_delete_refund' => __( 'Are you sure you wish to delete this refund? This action cannot be undone.', 'woocommerce' ),
|
||||
|
|
|
@ -207,6 +207,22 @@ class WC_Admin_Webhooks_Table_List extends WP_List_Table {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process bulk actions.
|
||||
*/
|
||||
public function process_bulk_action() {
|
||||
$action = $this->current_action();
|
||||
$webhooks = isset( $_REQUEST['webhook'] ) ? array_map( 'absint', (array) $_REQUEST['webhook'] ) : array(); // WPCS: input var okay, CSRF ok.
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to edit Webhooks', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
if ( 'delete' === $action ) {
|
||||
WC_Admin_Webhooks::bulk_delete( $webhooks );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the table navigation above or below the table.
|
||||
* Included to remove extra nonce input.
|
||||
|
|
|
@ -150,7 +150,7 @@ class WC_Admin_Webhooks {
|
|||
*
|
||||
* @param array $webhooks List of webhooks IDs.
|
||||
*/
|
||||
private function bulk_delete( $webhooks ) {
|
||||
public static function bulk_delete( $webhooks ) {
|
||||
foreach ( $webhooks as $webhook_id ) {
|
||||
$webhook = new WC_Webhook( (int) $webhook_id );
|
||||
$webhook->delete( true );
|
||||
|
@ -179,27 +179,6 @@ class WC_Admin_Webhooks {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk actions.
|
||||
*/
|
||||
private function bulk_actions() {
|
||||
check_admin_referer( 'woocommerce-settings' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_die( esc_html__( 'You do not have permission to edit Webhooks', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
if ( isset( $_REQUEST['action'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$webhooks = isset( $_REQUEST['webhook'] ) ? array_map( 'absint', (array) $_REQUEST['webhook'] ) : array(); // WPCS: input var okay, CSRF ok.
|
||||
|
||||
$action = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); // WPCS: input var okay, CSRF ok.
|
||||
|
||||
if ( 'delete' === $action ) {
|
||||
$this->bulk_delete( $webhooks );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Webhooks admin actions.
|
||||
*/
|
||||
|
@ -210,11 +189,6 @@ class WC_Admin_Webhooks {
|
|||
$this->save();
|
||||
}
|
||||
|
||||
// Bulk actions.
|
||||
if ( isset( $_REQUEST['action'] ) && isset( $_REQUEST['webhook'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$this->bulk_actions();
|
||||
}
|
||||
|
||||
// Delete webhook.
|
||||
if ( isset( $_GET['delete'] ) ) { // WPCS: input var okay, CSRF ok.
|
||||
$this->delete();
|
||||
|
@ -299,6 +273,7 @@ class WC_Admin_Webhooks {
|
|||
$count = count( $data_store->get_webhooks_ids() );
|
||||
|
||||
if ( 0 < $count ) {
|
||||
$webhooks_table_list->process_bulk_action();
|
||||
$webhooks_table_list->prepare_items();
|
||||
|
||||
echo '<input type="hidden" name="page" value="wc-settings" />';
|
||||
|
|
|
@ -222,7 +222,12 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
|
|||
* Render columm: order_date.
|
||||
*/
|
||||
protected function render_order_date_column() {
|
||||
$order_timestamp = $this->object->get_date_created()->getTimestamp();
|
||||
$order_timestamp = $this->object->get_date_created() ? $this->object->get_date_created()->getTimestamp() : '';
|
||||
|
||||
if ( ! $order_timestamp ) {
|
||||
echo '–';
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the order was created within the last 24 hours, and not in the future.
|
||||
if ( $order_timestamp > strtotime( '-1 day', current_time( 'timestamp', true ) ) && $order_timestamp <= current_time( 'timestamp', true ) ) {
|
||||
|
@ -428,6 +433,7 @@ class WC_Admin_List_Table_Orders extends WC_Admin_List_Table {
|
|||
'_line_tax',
|
||||
'method_id',
|
||||
'cost',
|
||||
'_reduced_stock',
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ $hidden_order_itemmeta = apply_filters(
|
|||
'_line_tax',
|
||||
'method_id',
|
||||
'cost',
|
||||
'_reduced_stock',
|
||||
)
|
||||
);
|
||||
?><div class="view">
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
<?php
|
||||
/**
|
||||
* Order items HTML for meta box.
|
||||
*
|
||||
* @package WooCommerce/Admin
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
global $wpdb;
|
||||
|
||||
// Get the payment gateway
|
||||
$payment_gateway = wc_get_payment_gateway_by_order( $order );
|
||||
|
||||
// Get line items
|
||||
$payment_gateway = wc_get_payment_gateway_by_order( $order );
|
||||
$line_items = $order->get_items( apply_filters( 'woocommerce_admin_order_item_types', 'line_item' ) );
|
||||
$discounts = $order->get_items( 'discount' );
|
||||
$line_items_fee = $order->get_items( 'fee' );
|
||||
|
@ -83,7 +83,9 @@ if ( wc_tax_enabled() ) {
|
|||
</tbody>
|
||||
<tbody id="order_refunds">
|
||||
<?php
|
||||
if ( $refunds = $order->get_refunds() ) {
|
||||
$refunds = $order->get_refunds();
|
||||
|
||||
if ( $refunds ) {
|
||||
foreach ( $refunds as $refund ) {
|
||||
include 'html-order-refund.php';
|
||||
}
|
||||
|
@ -93,18 +95,6 @@ if ( wc_tax_enabled() ) {
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="wc-order-data-row wc-order-item-bulk-edit" style="display:none;">
|
||||
<?php if ( $order->is_editable() ) : ?>
|
||||
<button type="button" class="button bulk-delete-items"><?php esc_html_e( 'Delete selected row(s)', 'woocommerce' ); ?></button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) : ?>
|
||||
<button type="button" class="button bulk-decrease-stock"><?php esc_html_e( 'Reduce stock', 'woocommerce' ); ?></button>
|
||||
<button type="button" class="button bulk-increase-stock"><?php esc_html_e( 'Increase stock', 'woocommerce' ); ?></button>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php do_action( 'woocommerce_admin_order_item_bulk_actions', $order ); ?>
|
||||
</div>
|
||||
<div class="wc-order-data-row wc-order-totals-items wc-order-items-editable">
|
||||
<?php
|
||||
$coupons = $order->get_items( 'coupon' );
|
||||
|
@ -118,7 +108,7 @@ if ( wc_tax_enabled() ) {
|
|||
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_title = %s AND post_type = 'shop_coupon' AND post_status = 'publish' LIMIT 1;", $item->get_code() ) );
|
||||
$class = $order->is_editable() ? 'code editable' : 'code';
|
||||
?>
|
||||
<li class="<?php echo $class; ?>">
|
||||
<li class="<?php echo esc_attr( $class ); ?>">
|
||||
<?php if ( $post_id ) : ?>
|
||||
<?php
|
||||
$post_url = apply_filters( 'woocommerce_admin_order_item_coupon_url', add_query_arg(
|
||||
|
@ -151,7 +141,7 @@ if ( wc_tax_enabled() ) {
|
|||
<td class="label"><?php esc_html_e( 'Discount:', 'woocommerce' ); ?></td>
|
||||
<td width="1%"></td>
|
||||
<td class="total">
|
||||
<?php echo wc_price( $order->get_total_discount(), array( 'currency' => $order->get_currency() ) ); ?>
|
||||
<?php echo wc_price( $order->get_total_discount(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok. ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
|
@ -166,9 +156,9 @@ if ( wc_tax_enabled() ) {
|
|||
<?php
|
||||
$refunded = $order->get_total_shipping_refunded();
|
||||
if ( $refunded > 0 ) {
|
||||
echo '<del>' . strip_tags( wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ) ) . '</del> <ins>' . wc_price( $order->get_shipping_total() - $refunded, array( 'currency' => $order->get_currency() ) ) . '</ins>';
|
||||
echo '<del>' . strip_tags( wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ) ) . '</del> <ins>' . wc_price( $order->get_shipping_total() - $refunded, array( 'currency' => $order->get_currency() ) ) . '</ins>'; // WPCS: XSS ok.
|
||||
} else {
|
||||
echo wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) );
|
||||
echo wc_price( $order->get_shipping_total(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok.
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
|
@ -186,9 +176,9 @@ if ( wc_tax_enabled() ) {
|
|||
<?php
|
||||
$refunded = $order->get_total_tax_refunded_by_rate_id( $tax->rate_id );
|
||||
if ( $refunded > 0 ) {
|
||||
echo '<del>' . strip_tags( $tax->formatted_amount ) . '</del> <ins>' . wc_price( WC_Tax::round( $tax->amount, wc_get_price_decimals() ) - WC_Tax::round( $refunded, wc_get_price_decimals() ), array( 'currency' => $order->get_currency() ) ) . '</ins>';
|
||||
echo '<del>' . strip_tags( $tax->formatted_amount ) . '</del> <ins>' . wc_price( WC_Tax::round( $tax->amount, wc_get_price_decimals() ) - WC_Tax::round( $refunded, wc_get_price_decimals() ), array( 'currency' => $order->get_currency() ) ) . '</ins>'; // WPCS: XSS ok.
|
||||
} else {
|
||||
echo $tax->formatted_amount;
|
||||
echo wp_kses_post( $tax->formatted_amount );
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
|
@ -202,7 +192,7 @@ if ( wc_tax_enabled() ) {
|
|||
<td class="label"><?php esc_html_e( 'Total', 'woocommerce' ); ?>:</td>
|
||||
<td width="1%"></td>
|
||||
<td class="total">
|
||||
<?php echo $order->get_formatted_order_total(); ?>
|
||||
<?php echo $order->get_formatted_order_total(); // WPCS: XSS ok. ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -212,7 +202,7 @@ if ( wc_tax_enabled() ) {
|
|||
<tr>
|
||||
<td class="label refunded-total"><?php esc_html_e( 'Refunded', 'woocommerce' ); ?>:</td>
|
||||
<td width="1%"></td>
|
||||
<td class="total refunded-total">-<?php echo wc_price( $order->get_total_refunded(), array( 'currency' => $order->get_currency() ) ); ?></td>
|
||||
<td class="total refunded-total">-<?php echo wc_price( $order->get_total_refunded(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok. ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
|
||||
|
@ -235,7 +225,7 @@ if ( wc_tax_enabled() ) {
|
|||
<button type="button" class="button refund-items"><?php esc_html_e( 'Refund', 'woocommerce' ); ?></button>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
// allow adding custom buttons
|
||||
// Allow adding custom buttons.
|
||||
do_action( 'woocommerce_order_item_add_action_buttons', $order );
|
||||
?>
|
||||
<?php if ( $order->is_editable() ) : ?>
|
||||
|
@ -251,7 +241,7 @@ if ( wc_tax_enabled() ) {
|
|||
<button type="button" class="button add-order-tax"><?php esc_html_e( 'Add tax', 'woocommerce' ); ?></button>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
// allow adding custom buttons
|
||||
// Allow adding custom buttons.
|
||||
do_action( 'woocommerce_order_item_add_line_buttons', $order );
|
||||
?>
|
||||
<button type="button" class="button cancel-action"><?php esc_html_e( 'Cancel', 'woocommerce' ); ?></button>
|
||||
|
@ -268,11 +258,11 @@ if ( wc_tax_enabled() ) {
|
|||
<?php endif; ?>
|
||||
<tr>
|
||||
<td class="label"><?php esc_html_e( 'Amount already refunded', 'woocommerce' ); ?>:</td>
|
||||
<td class="total">-<?php echo wc_price( $order->get_total_refunded(), array( 'currency' => $order->get_currency() ) ); ?></td>
|
||||
<td class="total">-<?php echo wc_price( $order->get_total_refunded(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok. ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label"><?php esc_html_e( 'Total available to refund', 'woocommerce' ); ?>:</td>
|
||||
<td class="total"><?php echo wc_price( $order->get_total() - $order->get_total_refunded(), array( 'currency' => $order->get_currency() ) ); ?></td>
|
||||
<td class="total"><?php echo wc_price( $order->get_total() - $order->get_total_refunded(), array( 'currency' => $order->get_currency() ) ); // WPCS: XSS ok. ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label"><label for="refund_amount"><?php esc_html_e( 'Refund amount', 'woocommerce' ); ?>:</label></td>
|
||||
|
@ -301,7 +291,7 @@ if ( wc_tax_enabled() ) {
|
|||
}
|
||||
?>
|
||||
<?php /* translators: refund amount */ ?>
|
||||
<button type="button" class="button button-primary do-manual-refund tips" data-tip="<?php esc_attr_e( 'You will need to manually issue a refund through your payment gateway after using this.', 'woocommerce' ); ?>"><?php printf( esc_html__( 'Refund %s manually', 'woocommerce' ), $refund_amount ); ?></button>
|
||||
<button type="button" class="button button-primary do-manual-refund tips" data-tip="<?php esc_attr_e( 'You will need to manually issue a refund through your payment gateway after using this.', 'woocommerce' ); ?>"><?php printf( esc_html__( 'Refund %s manually', 'woocommerce' ), wp_kses_post( $refund_amount ) ); ?></button>
|
||||
<button type="button" class="button cancel-action"><?php esc_html_e( 'Cancel', 'woocommerce' ); ?></button>
|
||||
<input type="hidden" id="refunded_amount" name="refunded_amount" value="<?php echo esc_attr( $order->get_total_refunded() ); ?>" />
|
||||
<div class="clear"></div>
|
||||
|
@ -321,7 +311,24 @@ if ( wc_tax_enabled() ) {
|
|||
</header>
|
||||
<article>
|
||||
<form action="" method="post">
|
||||
<select class="wc-product-search" multiple="multiple" style="width: 50%;" id="add_item_id" name="add_order_items[]" data-placeholder="<?php esc_attr_e( 'Search for a product…', 'woocommerce' ); ?>"></select>
|
||||
<table class="widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Product', 'woocommerce' ); ?></th>
|
||||
<th><?php esc_html_e( 'Quantity', 'woocommerce' ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<?php
|
||||
$row = '
|
||||
<td><select class="wc-product-search" name="item_id" data-allow_clear="true" data-display_stock="true" data-placeholder="' . esc_attr__( 'Search for a product…', 'woocommerce' ) . '"></select></td>
|
||||
<td><input type="number" step="1" min="0" max="9999" autocomplete="off" name="item_qty" placeholder="1" size="4" class="quantity" /></td>';
|
||||
?>
|
||||
<tbody data-row="<?php echo esc_attr( $row ); ?>">
|
||||
<tr>
|
||||
<?php echo $row; // WPCS: XSS ok. ?>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
</article>
|
||||
<footer>
|
||||
|
@ -369,7 +376,7 @@ if ( wc_tax_enabled() ) {
|
|||
<td>' . WC_Tax::get_rate_code( $rate ) . '</td>
|
||||
<td>' . WC_Tax::get_rate_percent( $rate ) . '</td>
|
||||
</tr>
|
||||
';
|
||||
'; // WPCS: XSS ok.
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
|
|
|
@ -1064,9 +1064,9 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
|
|||
$images = is_array( $images ) ? array_filter( $images ) : array();
|
||||
|
||||
if ( ! empty( $images ) ) {
|
||||
$gallery = array();
|
||||
$gallery_positions = array();
|
||||
|
||||
foreach ( $images as $image ) {
|
||||
foreach ( $images as $index => $image ) {
|
||||
$attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0;
|
||||
|
||||
if ( 0 === $attachment_id && isset( $image['src'] ) ) {
|
||||
|
@ -1088,11 +1088,7 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
|
|||
throw new WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 );
|
||||
}
|
||||
|
||||
if ( isset( $image['position'] ) && 0 === absint( $image['position'] ) ) {
|
||||
$product->set_image_id( $attachment_id );
|
||||
} else {
|
||||
$gallery[] = $attachment_id;
|
||||
}
|
||||
$gallery_positions[ $attachment_id ] = absint( isset( $image['position'] ) ? $image['position'] : $index );
|
||||
|
||||
// Set the image alt if present.
|
||||
if ( ! empty( $image['alt'] ) ) {
|
||||
|
@ -1115,6 +1111,17 @@ class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller {
|
|||
}
|
||||
}
|
||||
|
||||
// Sort images and get IDs in correct order.
|
||||
asort( $gallery_positions );
|
||||
|
||||
// Get gallery in correct order.
|
||||
$gallery = array_keys( $gallery_positions );
|
||||
|
||||
// Featured image is in position 0.
|
||||
$image_id = array_shift( $gallery );
|
||||
|
||||
// Set images.
|
||||
$product->set_image_id( $image_id );
|
||||
$product->set_gallery_image_ids( $gallery );
|
||||
} else {
|
||||
$product->set_image_id( '' );
|
||||
|
|
|
@ -360,8 +360,6 @@ class WC_API_Orders extends WC_API_Resource {
|
|||
public function create_order( $data ) {
|
||||
global $wpdb;
|
||||
|
||||
wc_transaction_query( 'start' );
|
||||
|
||||
try {
|
||||
if ( ! isset( $data['order'] ) ) {
|
||||
throw new WC_API_Exception( 'woocommerce_api_missing_order_data', sprintf( __( 'No %1$s data specified to create %1$s', 'woocommerce' ), 'order' ), 400 );
|
||||
|
@ -466,15 +464,10 @@ class WC_API_Orders extends WC_API_Resource {
|
|||
do_action( 'woocommerce_api_create_order', $order->get_id(), $data, $this );
|
||||
do_action( 'woocommerce_new_order', $order->get_id() );
|
||||
|
||||
wc_transaction_query( 'commit' );
|
||||
|
||||
return $this->get_order( $order->get_id() );
|
||||
|
||||
} catch ( WC_Data_Exception $e ) {
|
||||
wc_transaction_query( 'rollback' );
|
||||
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => 400 ) );
|
||||
} catch ( WC_API_Exception $e ) {
|
||||
wc_transaction_query( 'rollback' );
|
||||
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -389,16 +389,12 @@ class WC_API_Orders extends WC_API_Resource {
|
|||
* Create an order
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param array $data raw order data
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function create_order( $data ) {
|
||||
global $wpdb;
|
||||
|
||||
wc_transaction_query( 'start' );
|
||||
|
||||
try {
|
||||
if ( ! isset( $data['order'] ) ) {
|
||||
throw new WC_API_Exception( 'woocommerce_api_missing_order_data', sprintf( __( 'No %1$s data specified to create %1$s', 'woocommerce' ), 'order' ), 400 );
|
||||
|
@ -508,15 +504,10 @@ class WC_API_Orders extends WC_API_Resource {
|
|||
do_action( 'woocommerce_api_create_order', $order->get_id(), $data, $this );
|
||||
do_action( 'woocommerce_new_order', $order->get_id() );
|
||||
|
||||
wc_transaction_query( 'commit' );
|
||||
|
||||
return $this->get_order( $order->get_id() );
|
||||
|
||||
} catch ( WC_Data_Exception $e ) {
|
||||
wc_transaction_query( 'rollback' );
|
||||
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => 400 ) );
|
||||
} catch ( WC_API_Exception $e ) {
|
||||
wc_transaction_query( 'rollback' );
|
||||
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -847,17 +847,20 @@ class WC_AJAX {
|
|||
}
|
||||
|
||||
try {
|
||||
$order_id = absint( $_POST['order_id'] );
|
||||
if ( ! isset( $_POST['order_id'] ) ) {
|
||||
throw new Exception( __( 'Invalid order', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
$order_id = absint( wp_unslash( $_POST['order_id'] ) ); // WPCS: input var ok.
|
||||
$order = wc_get_order( $order_id );
|
||||
$items_to_add = wp_parse_id_list( is_array( $_POST['item_to_add'] ) ? $_POST['item_to_add'] : array( $_POST['item_to_add'] ) );
|
||||
$items = ( ! empty( $_POST['items'] ) ) ? $_POST['items'] : '';
|
||||
$added_items = array();
|
||||
|
||||
if ( ! $order ) {
|
||||
throw new Exception( __( 'Invalid order', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
// If we passed through items it means we need to save first before adding a new one.
|
||||
$items = ( ! empty( $_POST['items'] ) ) ? $_POST['items'] : '';
|
||||
|
||||
if ( ! empty( $items ) ) {
|
||||
$save_items = array();
|
||||
parse_str( $items, $save_items );
|
||||
|
@ -865,23 +868,28 @@ class WC_AJAX {
|
|||
wc_save_order_items( $order->get_id(), $save_items );
|
||||
}
|
||||
|
||||
foreach ( $items_to_add as $item_to_add ) {
|
||||
$items_to_add = array_filter( wp_unslash( (array) $_POST['data'] ) );
|
||||
|
||||
if ( ! in_array( get_post_type( $item_to_add ), array( 'product', 'product_variation' ) ) ) {
|
||||
// Add items to order.
|
||||
foreach ( $items_to_add as $item ) {
|
||||
if ( ! isset( $item['id'], $item['qty'] ) || empty( $item['id'] ) ) {
|
||||
continue;
|
||||
}
|
||||
$product_id = absint( $item['id'] );
|
||||
$qty = wc_stock_amount( $item['qty'] );
|
||||
$product = wc_get_product( $product_id );
|
||||
|
||||
$item_id = $order->add_product( wc_get_product( $item_to_add ) );
|
||||
$item = apply_filters( 'woocommerce_ajax_order_item', $order->get_item( $item_id ), $item_id );
|
||||
if ( ! $product ) {
|
||||
throw new Exception( __( 'Invalid product ID', 'woocommerce' ) . ' ' . $product_id );
|
||||
}
|
||||
|
||||
$item_id = $order->add_product( $product, $qty );
|
||||
$item = apply_filters( 'woocommerce_ajax_order_item', $order->get_item( $item_id ), $item_id );
|
||||
$added_items[ $item_id ] = $item;
|
||||
|
||||
do_action( 'woocommerce_ajax_add_order_item_meta', $item_id, $item, $order );
|
||||
}
|
||||
|
||||
$last_item = ! empty( $added_items ) ? end( $added_items ) : null;
|
||||
wc_do_deprecated_action( 'woocommerce_ajax_added_order_items', array( is_a( $last_item, 'WC_Order_Item' ) ? $last_item->get_id() : null, $last_item, $order ), '3.4', 'woocommerce_ajax_order_items_added action instead.' );
|
||||
|
||||
do_action( 'woocommerce_ajax_order_items_added', $added_items, $order );
|
||||
|
||||
$data = get_post_meta( $order_id );
|
||||
|
@ -1174,96 +1182,6 @@ class WC_AJAX {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce order item stock.
|
||||
*/
|
||||
public static function reduce_order_item_stock() {
|
||||
check_ajax_referer( 'order-item', 'security' );
|
||||
if ( ! current_user_can( 'edit_shop_orders' ) ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
$order_id = absint( $_POST['order_id'] );
|
||||
$order_item_ids = isset( $_POST['order_item_ids'] ) ? $_POST['order_item_ids'] : array();
|
||||
$order_item_qty = isset( $_POST['order_item_qty'] ) ? $_POST['order_item_qty'] : array();
|
||||
$order = wc_get_order( $order_id );
|
||||
$order_items = $order->get_items();
|
||||
$return = array();
|
||||
if ( $order && ! empty( $order_items ) && sizeof( $order_item_ids ) > 0 ) {
|
||||
foreach ( $order_items as $item_id => $order_item ) {
|
||||
// Only reduce checked items
|
||||
if ( ! in_array( $item_id, $order_item_ids ) ) {
|
||||
continue;
|
||||
}
|
||||
$_product = $order_item->get_product();
|
||||
if ( $_product && $_product->exists() && $_product->managing_stock() && isset( $order_item_qty[ $item_id ] ) && $order_item_qty[ $item_id ] > 0 ) {
|
||||
$stock_change = apply_filters( 'woocommerce_reduce_order_stock_quantity', $order_item_qty[ $item_id ], $item_id );
|
||||
$new_stock = wc_update_product_stock( $_product, $stock_change, 'decrease' );
|
||||
$item_name = $_product->get_formatted_name();
|
||||
$return[] = array(
|
||||
'note' => sprintf( wp_kses_post( __( '%1$s stock reduced from %2$s to %3$s.', 'woocommerce' ) ), $item_name, $new_stock + $stock_change, $new_stock ),
|
||||
'success' => true,
|
||||
);
|
||||
}
|
||||
}
|
||||
do_action( 'woocommerce_reduce_order_stock', $order );
|
||||
|
||||
if ( empty( $return ) ) {
|
||||
$return[] = array(
|
||||
'note' => wp_kses_post( __( 'No products had their stock reduced - they may not have stock management enabled.', 'woocommerce' ) ),
|
||||
'success' => false,
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success( $return );
|
||||
}
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase order item stock.
|
||||
*/
|
||||
public static function increase_order_item_stock() {
|
||||
check_ajax_referer( 'order-item', 'security' );
|
||||
if ( ! current_user_can( 'edit_shop_orders' ) ) {
|
||||
wp_die( -1 );
|
||||
}
|
||||
$order_id = absint( $_POST['order_id'] );
|
||||
$order_item_ids = isset( $_POST['order_item_ids'] ) ? $_POST['order_item_ids'] : array();
|
||||
$order_item_qty = isset( $_POST['order_item_qty'] ) ? $_POST['order_item_qty'] : array();
|
||||
$order = wc_get_order( $order_id );
|
||||
$order_items = $order->get_items();
|
||||
$return = array();
|
||||
if ( $order && ! empty( $order_items ) && sizeof( $order_item_ids ) > 0 ) {
|
||||
foreach ( $order_items as $item_id => $order_item ) {
|
||||
// Only reduce checked items
|
||||
if ( ! in_array( $item_id, $order_item_ids ) ) {
|
||||
continue;
|
||||
}
|
||||
$_product = $order_item->get_product();
|
||||
if ( $_product && $_product->exists() && $_product->managing_stock() && isset( $order_item_qty[ $item_id ] ) && $order_item_qty[ $item_id ] > 0 ) {
|
||||
$old_stock = $_product->get_stock_quantity();
|
||||
$stock_change = apply_filters( 'woocommerce_restore_order_stock_quantity', $order_item_qty[ $item_id ], $item_id );
|
||||
$new_quantity = wc_update_product_stock( $_product, $stock_change, 'increase' );
|
||||
$item_name = $_product->get_formatted_name();
|
||||
$return[] = array(
|
||||
'note' => sprintf( wp_kses_post( __( '%1$s stock increased from %2$s to %3$s.', 'woocommerce' ) ), $item_name, $old_stock, $new_quantity ),
|
||||
'success' => true,
|
||||
);
|
||||
}
|
||||
}
|
||||
do_action( 'woocommerce_restore_order_stock', $order );
|
||||
if ( empty( $return ) ) {
|
||||
$return[] = array(
|
||||
'note' => wp_kses_post( __( 'No products had their stock increased - they may not have stock management enabled.', 'woocommerce' ) ),
|
||||
'success' => false,
|
||||
);
|
||||
}
|
||||
|
||||
wp_send_json_success( $return );
|
||||
}
|
||||
wp_send_json_error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calc line tax.
|
||||
*/
|
||||
|
@ -1421,8 +1339,14 @@ class WC_AJAX {
|
|||
wp_die();
|
||||
}
|
||||
|
||||
if ( ! empty( $_GET['limit'] ) ) {
|
||||
$limit = absint( $_GET['limit'] );
|
||||
} else {
|
||||
$limit = absint( apply_filters( 'woocommerce_json_search_limit', 30 ) );
|
||||
}
|
||||
|
||||
$data_store = WC_Data_Store::load( 'product' );
|
||||
$ids = $data_store->search_products( $term, '', (bool) $include_variations );
|
||||
$ids = $data_store->search_products( $term, '', (bool) $include_variations, false, $limit );
|
||||
|
||||
if ( ! empty( $_GET['exclude'] ) ) {
|
||||
$ids = array_diff( $ids, (array) $_GET['exclude'] );
|
||||
|
@ -1432,15 +1356,18 @@ class WC_AJAX {
|
|||
$ids = array_intersect( $ids, (array) $_GET['include'] );
|
||||
}
|
||||
|
||||
if ( ! empty( $_GET['limit'] ) ) {
|
||||
$ids = array_slice( $ids, 0, absint( $_GET['limit'] ) );
|
||||
}
|
||||
|
||||
$product_objects = array_filter( array_map( 'wc_get_product', $ids ), 'wc_products_array_filter_readable' );
|
||||
$products = array();
|
||||
|
||||
foreach ( $product_objects as $product_object ) {
|
||||
$products[ $product_object->get_id() ] = rawurldecode( $product_object->get_formatted_name() );
|
||||
$formatted_name = $product_object->get_formatted_name();
|
||||
$managing_stock = $product_object->managing_stock();
|
||||
|
||||
if ( $managing_stock && ! empty( $_GET['display_stock'] ) ) {
|
||||
$formatted_name .= ' – ' . wc_format_stock_for_display( $product_object );
|
||||
}
|
||||
|
||||
$products[ $product_object->get_id() ] = rawurldecode( $formatted_name );
|
||||
}
|
||||
|
||||
wp_send_json( apply_filters( 'woocommerce_json_search_found_products', $products ) );
|
||||
|
|
|
@ -134,17 +134,35 @@ class WC_Cache_Helper {
|
|||
public static function get_transient_version( $group, $refresh = false ) {
|
||||
$transient_name = $group . '-transient-version';
|
||||
$transient_value = get_transient( $transient_name );
|
||||
$transient_value = strval( $transient_value ? $transient_value : '' );
|
||||
|
||||
if ( false === $transient_value || true === $refresh ) {
|
||||
self::delete_version_transients( $transient_value );
|
||||
if ( '' === $transient_value || true === $refresh ) {
|
||||
$old_transient_value = $transient_value;
|
||||
$transient_value = (string) time();
|
||||
|
||||
$transient_value = time();
|
||||
if ( $old_transient_value === $transient_value ) {
|
||||
// Time did not change but transient needs flushing now.
|
||||
self::delete_version_transients( $transient_value );
|
||||
} else {
|
||||
self::queue_delete_version_transients( $transient_value );
|
||||
}
|
||||
|
||||
set_transient( $transient_name, $transient_value );
|
||||
}
|
||||
return $transient_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a cleanup event for version transients.
|
||||
*
|
||||
* @param string $version Version of the transient to remove.
|
||||
*/
|
||||
protected static function queue_delete_version_transients( $version = '' ) {
|
||||
if ( ! wp_using_ext_object_cache() && ! empty( $version ) ) {
|
||||
wp_schedule_single_event( time() + 30, 'delete_version_transients', array( $version ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the transient version increases, this is used to remove all past transients to avoid filling the DB.
|
||||
*
|
||||
|
@ -165,9 +183,9 @@ class WC_Cache_Helper {
|
|||
|
||||
$affected = $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s ORDER BY option_id LIMIT %d;", '\_transient\_%' . $version, $limit ) ); // WPCS: cache ok, db call ok.
|
||||
|
||||
// If affected rows is equal to limit, there are more rows to delete. Delete in 10 secs.
|
||||
// If affected rows is equal to limit, there are more rows to delete. Delete in 30 secs.
|
||||
if ( $affected === $limit ) {
|
||||
wp_schedule_single_event( time() + 10, 'delete_version_transients', array( $version ) );
|
||||
self::queue_delete_version_transients( $version );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -757,66 +757,35 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
* @return bool|WP_Error
|
||||
*/
|
||||
public function check_cart_item_stock() {
|
||||
global $wpdb;
|
||||
|
||||
$error = new WP_Error();
|
||||
$product_qty_in_cart = $this->get_cart_item_quantities();
|
||||
$error = new WP_Error();
|
||||
$product_qty_in_cart = $this->get_cart_item_quantities();
|
||||
$hold_stock_minutes = (int) get_option( 'woocommerce_hold_stock_minutes', 0 );
|
||||
$current_session_order_id = isset( WC()->session->order_awaiting_payment ) ? absint( WC()->session->order_awaiting_payment ) : 0;
|
||||
|
||||
foreach ( $this->get_cart() as $cart_item_key => $values ) {
|
||||
$product = $values['data'];
|
||||
|
||||
/**
|
||||
* Check stock based on stock-status.
|
||||
*/
|
||||
// Check stock based on stock-status.
|
||||
if ( ! $product->is_in_stock() ) {
|
||||
/* translators: %s: product name */
|
||||
$error->add( 'out-of-stock', sprintf( __( 'Sorry, "%s" is not in stock. Please edit your cart and try again. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name() ) );
|
||||
return $error;
|
||||
}
|
||||
|
||||
if ( ! $product->managing_stock() ) {
|
||||
// We only need to check products managing stock, with a limited stock qty.
|
||||
if ( ! $product->managing_stock() || $product->backorders_allowed() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check stock based on all items in the cart.
|
||||
*/
|
||||
if ( ! $product->has_enough_stock( $product_qty_in_cart[ $product->get_stock_managed_by_id() ] ) ) {
|
||||
// Check stock based on all items in the cart and consider any held stock within pending orders.
|
||||
$held_stock = wc_get_held_stock_quantity( $product, $current_session_order_id );
|
||||
$required_stock = $product_qty_in_cart[ $product->get_stock_managed_by_id() ];
|
||||
|
||||
if ( $product->get_stock_quantity() < ( $held_stock + $required_stock ) ) {
|
||||
/* translators: 1: product name 2: quantity in stock */
|
||||
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s in stock). Please edit your cart and try again. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity(), $product ) ) );
|
||||
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s available). We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity() - $held_stock, $product ) ) );
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finally consider any held stock, from pending orders.
|
||||
*/
|
||||
if ( get_option( 'woocommerce_hold_stock_minutes' ) > 0 && ! $product->backorders_allowed() ) {
|
||||
$order_id = isset( WC()->session->order_awaiting_payment ) ? absint( WC()->session->order_awaiting_payment ) : 0;
|
||||
$held_stock = $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT SUM( order_item_meta.meta_value ) AS held_qty
|
||||
FROM {$wpdb->posts} AS posts
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_items as order_items ON posts.ID = order_items.order_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta2 ON order_items.order_item_id = order_item_meta2.order_item_id
|
||||
WHERE order_item_meta.meta_key = '_qty'
|
||||
AND order_item_meta2.meta_key = %s AND order_item_meta2.meta_value = %d
|
||||
AND posts.post_type IN ( '" . implode( "','", wc_get_order_types() ) . "' )
|
||||
AND posts.post_status = 'wc-pending'
|
||||
AND posts.ID != %d;",
|
||||
'variation' === get_post_type( $product->get_stock_managed_by_id() ) ? '_variation_id' : '_product_id',
|
||||
$product->get_stock_managed_by_id(),
|
||||
$order_id
|
||||
)
|
||||
); // WPCS: unprepared SQL ok.
|
||||
|
||||
if ( $product->get_stock_quantity() < ( $held_stock + $product_qty_in_cart[ $product->get_stock_managed_by_id() ] ) ) {
|
||||
/* translators: 1: product name 2: minutes */
|
||||
$error->add( 'out-of-stock', sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order right now. Please try again in %2$d minutes or edit your cart and try again. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name(), get_option( 'woocommerce_hold_stock_minutes' ) ) );
|
||||
return $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -499,11 +499,28 @@ class WC_Discounts {
|
|||
* @return int Total discounted.
|
||||
*/
|
||||
protected function apply_coupon_custom( $coupon, $items_to_apply ) {
|
||||
$limit_usage_qty = 0;
|
||||
$applied_count = 0;
|
||||
|
||||
if ( null !== $coupon->get_limit_usage_to_x_items() ) {
|
||||
$limit_usage_qty = $coupon->get_limit_usage_to_x_items();
|
||||
}
|
||||
|
||||
// Apply the coupon to each item.
|
||||
foreach ( $items_to_apply as $item ) {
|
||||
$discounted_price = $this->get_discounted_price_in_cents( $item );
|
||||
// Find out how much price is available to discount for the item.
|
||||
$discounted_price = $this->get_discounted_price_in_cents( $item );
|
||||
|
||||
// Get the price we actually want to discount, based on settings.
|
||||
$price_to_discount = wc_remove_number_precision( ( 'yes' === get_option( 'woocommerce_calc_discounts_sequentially', 'no' ) ) ? $discounted_price : $item->price );
|
||||
$discount = wc_add_number_precision( $coupon->get_discount_amount( $price_to_discount / $item->quantity, $item->object, true ) ) * $item->quantity;
|
||||
$discount = min( $discounted_price, $discount );
|
||||
|
||||
// See how many and what price to apply to.
|
||||
$apply_quantity = $limit_usage_qty && ( $limit_usage_qty - $applied_count ) < $item->quantity ? $limit_usage_qty - $applied_count : $item->quantity;
|
||||
$apply_quantity = max( 0, apply_filters( 'woocommerce_coupon_get_apply_quantity', $apply_quantity, $item, $coupon, $this ) );
|
||||
|
||||
// Run coupon calculations.
|
||||
$discount = wc_add_number_precision( $coupon->get_discount_amount( $price_to_discount / $item->quantity, $item->object, true ) ) * $apply_quantity;
|
||||
$discount = wc_round_discount( min( $discounted_price, $discount ), 0 );
|
||||
|
||||
// Store code and discount amount per item.
|
||||
$this->discounts[ $coupon->get_code() ][ $item->key ] += $discount;
|
||||
|
|
|
@ -102,8 +102,6 @@ class WC_Order extends WC_Abstract_Order {
|
|||
}
|
||||
|
||||
try {
|
||||
wc_transaction_query( 'start' );
|
||||
|
||||
do_action( 'woocommerce_pre_payment_complete', $this->get_id() );
|
||||
|
||||
if ( WC()->session ) {
|
||||
|
@ -124,20 +122,18 @@ class WC_Order extends WC_Abstract_Order {
|
|||
} else {
|
||||
do_action( 'woocommerce_payment_complete_order_status_' . $this->get_status(), $this->get_id() );
|
||||
}
|
||||
|
||||
wc_transaction_query( 'commit' );
|
||||
} catch ( Exception $e ) {
|
||||
wc_transaction_query( 'rollback' );
|
||||
|
||||
/**
|
||||
* If there was an error completing the payment, log to a file and add an order note so the admin can take action.
|
||||
*/
|
||||
$logger = wc_get_logger();
|
||||
$logger->error(
|
||||
sprintf( 'Payment complete of order #%d failed!', $this->get_id() ), array(
|
||||
sprintf( 'Error completing payment for order #%d', $this->get_id() ), array(
|
||||
'order' => $this,
|
||||
'error' => $e,
|
||||
)
|
||||
);
|
||||
$this->add_order_note( __( 'Payment complete event failed.', 'woocommerce' ) . ' ' . $e->getMessage() );
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -320,18 +316,12 @@ class WC_Order extends WC_Abstract_Order {
|
|||
}
|
||||
|
||||
try {
|
||||
wc_transaction_query( 'start' );
|
||||
|
||||
$this->set_status( $new_status, $note, $manual );
|
||||
$this->save();
|
||||
|
||||
wc_transaction_query( 'commit' );
|
||||
} catch ( Exception $e ) {
|
||||
wc_transaction_query( 'rollback' );
|
||||
|
||||
$logger = wc_get_logger();
|
||||
$logger->error(
|
||||
sprintf( 'Update status of order #%d failed!', $this->get_id() ), array(
|
||||
sprintf( 'Error updating status for order #%d', $this->get_id() ), array(
|
||||
'order' => $this,
|
||||
'error' => $e,
|
||||
)
|
||||
|
|
|
@ -39,16 +39,16 @@ class WC_Privacy extends WC_Abstract_Privacy {
|
|||
include_once 'class-wc-privacy-exporters.php';
|
||||
|
||||
// This hook registers WooCommerce data exporters.
|
||||
$this->add_exporter( 'woocommerce-customer-data', __( 'Customer Data', 'woocommerce' ), array( 'WC_Privacy_Exporters', 'customer_data_exporter' ) );
|
||||
$this->add_exporter( 'woocommerce-customer-orders', __( 'Customer Orders', 'woocommerce' ), array( 'WC_Privacy_Exporters', 'order_data_exporter' ) );
|
||||
$this->add_exporter( 'woocommerce-customer-downloads', __( 'Customer Downloads', 'woocommerce' ), array( 'WC_Privacy_Exporters', 'download_data_exporter' ) );
|
||||
$this->add_exporter( 'woocommerce-customer-tokens', __( 'Customer Payment Tokens', 'woocommerce' ), array( 'WC_Privacy_Exporters', 'customer_tokens_exporter' ) );
|
||||
$this->add_exporter( 'woocommerce-customer-data', __( 'WooCommerce Customer Data', 'woocommerce' ), array( 'WC_Privacy_Exporters', 'customer_data_exporter' ) );
|
||||
$this->add_exporter( 'woocommerce-customer-orders', __( 'WooCommerce Customer Orders', 'woocommerce' ), array( 'WC_Privacy_Exporters', 'order_data_exporter' ) );
|
||||
$this->add_exporter( 'woocommerce-customer-downloads', __( 'WooCommerce Customer Downloads', 'woocommerce' ), array( 'WC_Privacy_Exporters', 'download_data_exporter' ) );
|
||||
$this->add_exporter( 'woocommerce-customer-tokens', __( 'WooCommerce Customer Payment Tokens', 'woocommerce' ), array( 'WC_Privacy_Exporters', 'customer_tokens_exporter' ) );
|
||||
|
||||
// This hook registers WooCommerce data erasers.
|
||||
$this->add_eraser( 'woocommerce-customer-data', __( 'Customer Data', 'woocommerce' ), array( 'WC_Privacy_Erasers', 'customer_data_eraser' ) );
|
||||
$this->add_eraser( 'woocommerce-customer-orders', __( 'Customer Orders', 'woocommerce' ), array( 'WC_Privacy_Erasers', 'order_data_eraser' ) );
|
||||
$this->add_eraser( 'woocommerce-customer-downloads', __( 'Customer Downloads', 'woocommerce' ), array( 'WC_Privacy_Erasers', 'download_data_eraser' ) );
|
||||
$this->add_eraser( 'woocommerce-customer-tokens', __( 'Customer Payment Tokens', 'woocommerce' ), array( 'WC_Privacy_Erasers', 'customer_tokens_eraser' ) );
|
||||
$this->add_eraser( 'woocommerce-customer-data', __( 'WooCommerce Customer Data', 'woocommerce' ), array( 'WC_Privacy_Erasers', 'customer_data_eraser' ) );
|
||||
$this->add_eraser( 'woocommerce-customer-orders', __( 'WooCommerce Customer Orders', 'woocommerce' ), array( 'WC_Privacy_Erasers', 'order_data_eraser' ) );
|
||||
$this->add_eraser( 'woocommerce-customer-downloads', __( 'WooCommerce Customer Downloads', 'woocommerce' ), array( 'WC_Privacy_Erasers', 'download_data_eraser' ) );
|
||||
$this->add_eraser( 'woocommerce-customer-tokens', __( 'WooCommerce Customer Payment Tokens', 'woocommerce' ), array( 'WC_Privacy_Erasers', 'customer_tokens_eraser' ) );
|
||||
|
||||
// Cleanup orders daily - this is a callback on a daily cron event.
|
||||
add_action( 'woocommerce_cleanup_personal_data', array( $this, 'queue_cleanup_personal_data' ) );
|
||||
|
|
|
@ -426,6 +426,33 @@ class WC_Product_Variation extends WC_Product_Simple {
|
|||
* @param array $parent_data parent data array for this variation.
|
||||
*/
|
||||
public function set_parent_data( $parent_data ) {
|
||||
$parent_data = wp_parse_args( $parent_data, array(
|
||||
'title' => '',
|
||||
'status' => '',
|
||||
'sku' => '',
|
||||
'manage_stock' => 'no',
|
||||
'backorders' => 'no',
|
||||
'stock_quantity' => '',
|
||||
'weight' => '',
|
||||
'length' => '',
|
||||
'width' => '',
|
||||
'height' => '',
|
||||
'tax_class' => '',
|
||||
'shipping_class_id' => 0,
|
||||
'image_id' => 0,
|
||||
'purchase_note' => '',
|
||||
'catalog_visibility' => 'visible',
|
||||
) );
|
||||
|
||||
// Normalize tax class.
|
||||
$parent_data['tax_class'] = sanitize_title( $parent_data['tax_class'] );
|
||||
$parent_data['tax_class'] = 'standard' === $parent_data['tax_class'] ? '' : $parent_data['tax_class'];
|
||||
$valid_classes = $this->get_valid_tax_classes();
|
||||
|
||||
if ( ! in_array( $parent_data['tax_class'], $valid_classes, true ) ) {
|
||||
$parent_data['tax_class'] = '';
|
||||
}
|
||||
|
||||
$this->parent_data = $parent_data;
|
||||
}
|
||||
|
||||
|
|
|
@ -324,7 +324,7 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
|||
global $wpdb;
|
||||
|
||||
// Get from cache if available.
|
||||
$items = wp_cache_get( 'order-items-' . $order->get_id(), 'orders' );
|
||||
$items = 0 < $order->get_id() ? wp_cache_get( 'order-items-' . $order->get_id(), 'orders' ) : false;
|
||||
|
||||
if ( false === $items ) {
|
||||
$items = $wpdb->get_results(
|
||||
|
@ -333,7 +333,9 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
|
|||
foreach ( $items as $item ) {
|
||||
wp_cache_set( 'item-' . $item->order_item_id, $item, 'order-items' );
|
||||
}
|
||||
wp_cache_set( 'order-items-' . $order->get_id(), $items, 'orders' );
|
||||
if ( 0 < $order->get_id() ) {
|
||||
wp_cache_set( 'order-items-' . $order->get_id(), $items, 'orders' );
|
||||
}
|
||||
}
|
||||
|
||||
$items = wp_list_filter( $items, array( 'order_item_type' => $type ) );
|
||||
|
|
|
@ -594,19 +594,24 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
* @param WC_Product $product Product Object.
|
||||
*/
|
||||
protected function handle_updated_props( &$product ) {
|
||||
if ( in_array( 'regular_price', $this->updated_props, true ) || in_array( 'sale_price', $this->updated_props, true ) ) {
|
||||
if ( $product->get_sale_price( 'edit' ) >= $product->get_regular_price( 'edit' ) ) {
|
||||
update_post_meta( $product->get_id(), '_sale_price', '' );
|
||||
$product->set_sale_price( '' );
|
||||
$price_is_synced = $product->is_type( array( 'variable', 'grouped' ) );
|
||||
|
||||
if ( ! $price_is_synced ) {
|
||||
if ( in_array( 'regular_price', $this->updated_props, true ) || in_array( 'sale_price', $this->updated_props, true ) ) {
|
||||
if ( $product->get_sale_price( 'edit' ) >= $product->get_regular_price( 'edit' ) ) {
|
||||
update_post_meta( $product->get_id(), '_sale_price', '' );
|
||||
$product->set_sale_price( '' );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( in_array( 'date_on_sale_from', $this->updated_props, true ) || in_array( 'date_on_sale_to', $this->updated_props, true ) || in_array( 'regular_price', $this->updated_props, true ) || in_array( 'sale_price', $this->updated_props, true ) || in_array( 'product_type', $this->updated_props, true ) ) {
|
||||
if ( $product->is_on_sale( 'edit' ) ) {
|
||||
update_post_meta( $product->get_id(), '_price', $product->get_sale_price( 'edit' ) );
|
||||
$product->set_price( $product->get_sale_price( 'edit' ) );
|
||||
} else {
|
||||
update_post_meta( $product->get_id(), '_price', $product->get_regular_price( 'edit' ) );
|
||||
$product->set_price( $product->get_regular_price( 'edit' ) );
|
||||
|
||||
if ( in_array( 'date_on_sale_from', $this->updated_props, true ) || in_array( 'date_on_sale_to', $this->updated_props, true ) || in_array( 'regular_price', $this->updated_props, true ) || in_array( 'sale_price', $this->updated_props, true ) || in_array( 'product_type', $this->updated_props, true ) ) {
|
||||
if ( $product->is_on_sale( 'edit' ) ) {
|
||||
update_post_meta( $product->get_id(), '_price', $product->get_sale_price( 'edit' ) );
|
||||
$product->set_price( $product->get_sale_price( 'edit' ) );
|
||||
} else {
|
||||
update_post_meta( $product->get_id(), '_price', $product->get_regular_price( 'edit' ) );
|
||||
$product->set_price( $product->get_regular_price( 'edit' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1344,13 +1349,14 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
/**
|
||||
* Search product data for a term and return ids.
|
||||
*
|
||||
* @param string $term Search term.
|
||||
* @param string $type Type of product.
|
||||
* @param bool $include_variations Include variations in search or not.
|
||||
* @param bool $all_statuses Should we search all statuses or limit to published.
|
||||
* @param string $term Search term.
|
||||
* @param string $type Type of product.
|
||||
* @param bool $include_variations Include variations in search or not.
|
||||
* @param bool $all_statuses Should we search all statuses or limit to published.
|
||||
* @param null|int $limit Limit returned results. @since 3.5.0.
|
||||
* @return array of ids
|
||||
*/
|
||||
public function search_products( $term, $type = '', $include_variations = false, $all_statuses = false ) {
|
||||
public function search_products( $term, $type = '', $include_variations = false, $all_statuses = false, $limit = null ) {
|
||||
global $wpdb;
|
||||
|
||||
$post_types = $include_variations ? array( 'product', 'product_variation' ) : array( 'product' );
|
||||
|
@ -1358,6 +1364,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
$type_join = '';
|
||||
$type_where = '';
|
||||
$status_where = '';
|
||||
$limit_query = '';
|
||||
$term = wc_strtolower( $term );
|
||||
|
||||
// See if search term contains OR keywords.
|
||||
|
@ -1411,6 +1418,10 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
$status_where = " AND posts.post_status IN ('" . implode( "','", $post_statuses ) . "') ";
|
||||
}
|
||||
|
||||
if ( $limit ) {
|
||||
$limit_query = $wpdb->prepare( ' LIMIT %d ', $limit );
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.VIP.DirectDatabaseQuery.DirectQuery
|
||||
$search_results = $wpdb->get_results(
|
||||
// phpcs:disable
|
||||
|
@ -1421,7 +1432,9 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
$search_where
|
||||
$status_where
|
||||
$type_where
|
||||
ORDER BY posts.post_parent ASC, posts.post_title ASC"
|
||||
ORDER BY posts.post_parent ASC, posts.post_title ASC
|
||||
$limit_query
|
||||
"
|
||||
// phpcs:enable
|
||||
);
|
||||
|
||||
|
|
|
@ -74,11 +74,13 @@ class WC_Product_Grouped_Data_Store_CPT extends WC_Product_Data_Store_CPT implem
|
|||
foreach ( $product->get_children( 'edit' ) as $child_id ) {
|
||||
$child = wc_get_product( $child_id );
|
||||
if ( $child ) {
|
||||
$child_prices[] = $child->get_price();
|
||||
$child_prices[] = $child->get_price( 'edit' );
|
||||
}
|
||||
}
|
||||
$child_prices = array_filter( $child_prices );
|
||||
delete_post_meta( $product->get_id(), '_price' );
|
||||
delete_post_meta( $product->get_id(), '_sale_price' );
|
||||
delete_post_meta( $product->get_id(), '_regular_price' );
|
||||
|
||||
if ( ! empty( $child_prices ) ) {
|
||||
add_post_meta( $product->get_id(), '_price', min( $child_prices ) );
|
||||
|
|
|
@ -515,6 +515,8 @@ class WC_Product_Variable_Data_Store_CPT extends WC_Product_Data_Store_CPT imple
|
|||
$prices = $children ? array_unique( $wpdb->get_col( "SELECT meta_value FROM $wpdb->postmeta WHERE meta_key = '_price' AND post_id IN ( " . implode( ',', array_map( 'absint', $children ) ) . ' )' ) ) : array(); // phpcs:ignore WordPress.WP.PreparedSQL.NotPrepared
|
||||
|
||||
delete_post_meta( $product->get_id(), '_price' );
|
||||
delete_post_meta( $product->get_id(), '_sale_price' );
|
||||
delete_post_meta( $product->get_id(), '_regular_price' );
|
||||
|
||||
if ( $prices ) {
|
||||
sort( $prices );
|
||||
|
|
|
@ -364,9 +364,6 @@ class WC_Gateway_BACS extends WC_Payment_Gateway {
|
|||
$order->payment_complete();
|
||||
}
|
||||
|
||||
// Reduce stock levels.
|
||||
wc_reduce_stock_levels( $order_id );
|
||||
|
||||
// Remove cart.
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
|
|
|
@ -124,9 +124,6 @@ class WC_Gateway_Cheque extends WC_Payment_Gateway {
|
|||
$order->payment_complete();
|
||||
}
|
||||
|
||||
// Reduce stock levels.
|
||||
wc_reduce_stock_levels( $order_id );
|
||||
|
||||
// Remove cart.
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
|
|
|
@ -281,9 +281,6 @@ class WC_Gateway_COD extends WC_Payment_Gateway {
|
|||
$order->payment_complete();
|
||||
}
|
||||
|
||||
// Reduce stock levels.
|
||||
wc_reduce_stock_levels( $order_id );
|
||||
|
||||
// Remove cart.
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
|
|
|
@ -76,7 +76,6 @@ abstract class WC_Gateway_Paypal_Response {
|
|||
*/
|
||||
protected function payment_on_hold( $order, $reason = '' ) {
|
||||
$order->update_status( 'on-hold', $reason );
|
||||
wc_reduce_stock_levels( $order->get_id() );
|
||||
WC()->cart->empty_cart();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,9 +216,6 @@ class WC_Addons_Gateway_Simplify_Commerce extends WC_Gateway_Simplify_Commerce {
|
|||
throw new Simplify_ApiException( $error_msg );
|
||||
}
|
||||
|
||||
// Reduce stock levels
|
||||
wc_reduce_stock_levels( $order->get_id() );
|
||||
|
||||
// Remove cart
|
||||
WC()->cart->empty_cart();
|
||||
|
||||
|
|
|
@ -114,14 +114,43 @@ class WC_Shortcode_Checkout {
|
|||
throw new Exception( sprintf( __( 'This order’s status is “%s”—it cannot be paid for. Please contact us if you need assistance.', 'woocommerce' ), wc_get_order_status_name( $order->get_status() ) ) );
|
||||
}
|
||||
|
||||
// Ensure order items are still stocked.
|
||||
foreach ( $order->get_items() as $item_key => $item ) {
|
||||
if ( $item && is_callable( array( $item, 'get_product' ) ) ) {
|
||||
$product = $item->get_product();
|
||||
// Ensure order items are still stocked if paying for a failed order. Pending orders do not need this check because stock is held.
|
||||
if ( ! $order->has_status( 'pending' ) ) {
|
||||
$quantities = array();
|
||||
|
||||
if ( $product && ! apply_filters( 'woocommerce_pay_order_product_in_stock', $product->is_in_stock(), $product, $order ) ) {
|
||||
/* translators: %s: product name */
|
||||
throw new Exception( sprintf( __( 'Sorry, "%s" is no longer in stock so this order cannot be paid for. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name() ) );
|
||||
foreach ( $order->get_items() as $item_key => $item ) {
|
||||
if ( $item && is_callable( array( $item, 'get_product' ) ) ) {
|
||||
$product = $item->get_product();
|
||||
|
||||
if ( ! $product ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$quantities[ $product->get_stock_managed_by_id() ] = isset( $quantities[ $product->get_stock_managed_by_id() ] ) ? $quantities[ $product->get_stock_managed_by_id() ] + $item->get_quantity() : $item->get_quantity();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $order->get_items() as $item_key => $item ) {
|
||||
if ( $item && is_callable( array( $item, 'get_product' ) ) ) {
|
||||
$product = $item->get_product();
|
||||
|
||||
if ( ! $product ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! apply_filters( 'woocommerce_pay_order_product_in_stock', $product->is_in_stock(), $product, $order ) ) {
|
||||
/* translators: %s: product name */
|
||||
throw new Exception( sprintf( __( 'Sorry, "%s" is no longer in stock so this order cannot be paid for. We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name() ) );
|
||||
}
|
||||
|
||||
// Check stock based on all items in the cart and consider any held stock within pending orders.
|
||||
$held_stock = wc_get_held_stock_quantity( $product, $order->get_id() );
|
||||
$required_stock = $quantities[ $product->get_stock_managed_by_id() ];
|
||||
|
||||
if ( $product->get_stock_quantity() < ( $held_stock + $required_stock ) ) {
|
||||
/* translators: 1: product name 2: quantity in stock */
|
||||
throw new Exception( sprintf( __( 'Sorry, we do not have enough "%1$s" in stock to fulfill your order (%2$s available). We apologize for any inconvenience caused.', 'woocommerce' ), $product->get_name(), wc_format_stock_quantity_for_display( $product->get_stock_quantity() - $held_stock, $product ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,8 @@ function wc_rest_upload_image_from_url( $image_url ) {
|
|||
if ( ! $wp_filetype['type'] ) {
|
||||
$headers = wp_remote_retrieve_headers( $response );
|
||||
if ( isset( $headers['content-disposition'] ) && strstr( $headers['content-disposition'], 'filename=' ) ) {
|
||||
$disposition = end( explode( 'filename=', $headers['content-disposition'] ) );
|
||||
$content = explode( 'filename=', $headers['content-disposition'] );
|
||||
$disposition = end( $content );
|
||||
$disposition = sanitize_file_name( $disposition );
|
||||
$file_name = $disposition;
|
||||
} elseif ( isset( $headers['content-type'] ) && strstr( $headers['content-type'], 'image/' ) ) {
|
||||
|
|
|
@ -79,14 +79,60 @@ function wc_update_product_stock_status( $product_id, $status ) {
|
|||
*/
|
||||
function wc_maybe_reduce_stock_levels( $order_id ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( apply_filters( 'woocommerce_payment_complete_reduce_order_stock', $order && ! $order->get_data_store()->get_stock_reduced( $order_id ), $order_id ) ) {
|
||||
wc_reduce_stock_levels( $order );
|
||||
|
||||
if ( ! $order ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stock_reduced = $order->get_data_store()->get_stock_reduced( $order_id );
|
||||
$trigger_reduce = apply_filters( 'woocommerce_payment_complete_reduce_order_stock', ! $stock_reduced, $order_id );
|
||||
|
||||
// Only continue if we're reducing stock.
|
||||
if ( ! $trigger_reduce ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wc_reduce_stock_levels( $order );
|
||||
|
||||
// Ensure stock is marked as "reduced" in case payment complete or other stock actions are called.
|
||||
$order->get_data_store()->set_stock_reduced( $order_id, true );
|
||||
}
|
||||
add_action( 'woocommerce_payment_complete', 'wc_maybe_reduce_stock_levels' );
|
||||
add_action( 'woocommerce_order_status_completed', 'wc_maybe_reduce_stock_levels' );
|
||||
add_action( 'woocommerce_order_status_processing', 'wc_maybe_reduce_stock_levels' );
|
||||
add_action( 'woocommerce_order_status_on-hold', 'wc_maybe_reduce_stock_levels' );
|
||||
|
||||
/**
|
||||
* Reduce stock levels for items within an order.
|
||||
* When a payment is cancelled, restore stock.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param int $order_id Order ID.
|
||||
*/
|
||||
function wc_maybe_increase_stock_levels( $order_id ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
if ( ! $order ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stock_reduced = $order->get_data_store()->get_stock_reduced( $order_id );
|
||||
$trigger_reduce = (bool) $stock_reduced;
|
||||
|
||||
// Only continue if we're reducing stock.
|
||||
if ( ! $trigger_reduce ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wc_increase_stock_levels( $order );
|
||||
|
||||
// Ensure stock is marked as "reduced" in case payment complete or other stock actions are called.
|
||||
$order->get_data_store()->set_stock_reduced( $order_id, false );
|
||||
}
|
||||
add_action( 'woocommerce_order_status_cancelled', 'wc_maybe_increase_stock_levels' );
|
||||
add_action( 'woocommerce_order_status_pending', 'wc_maybe_increase_stock_levels' );
|
||||
|
||||
/**
|
||||
* Reduce stock levels for items within an order, if stock has not already been reduced for the items.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param int|WC_Order $order_id Order ID or order instance.
|
||||
|
@ -98,48 +144,175 @@ function wc_reduce_stock_levels( $order_id ) {
|
|||
} else {
|
||||
$order = wc_get_order( $order_id );
|
||||
}
|
||||
if ( 'yes' === get_option( 'woocommerce_manage_stock' ) && $order && apply_filters( 'woocommerce_can_reduce_order_stock', true, $order ) && count( $order->get_items() ) > 0 ) {
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
if ( ! $item->is_type( 'line_item' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$product = $item->get_product();
|
||||
// We need an order, and a store with stock management to continue.
|
||||
if ( ! $order || 'yes' !== get_option( 'woocommerce_manage_stock' ) || ! apply_filters( 'woocommerce_can_reduce_order_stock', true, $order ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $product && $product->managing_stock() ) {
|
||||
$qty = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $order, $item );
|
||||
$item_name = $product->get_formatted_name();
|
||||
$new_stock = wc_update_product_stock( $product, $qty, 'decrease' );
|
||||
$changes = array();
|
||||
|
||||
if ( ! is_wp_error( $new_stock ) ) {
|
||||
/* translators: 1: item name 2: old stock quantity 3: new stock quantity */
|
||||
$order->add_order_note( sprintf( __( '%1$s stock reduced from %2$s to %3$s.', 'woocommerce' ), $item_name, $new_stock + $qty, $new_stock ) );
|
||||
|
||||
// Get the latest product data.
|
||||
$product = wc_get_product( $product->get_id() );
|
||||
|
||||
if ( '' !== get_option( 'woocommerce_notify_no_stock_amount' ) && $new_stock <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
|
||||
do_action( 'woocommerce_no_stock', $product );
|
||||
} elseif ( '' !== get_option( 'woocommerce_notify_low_stock_amount' ) && $new_stock <= get_option( 'woocommerce_notify_low_stock_amount' ) ) {
|
||||
do_action( 'woocommerce_low_stock', $product );
|
||||
}
|
||||
|
||||
if ( $new_stock < 0 ) {
|
||||
do_action(
|
||||
'woocommerce_product_on_backorder', array(
|
||||
'product' => $product,
|
||||
'order_id' => $order_id,
|
||||
'quantity' => $qty,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Loop over all items.
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
if ( ! $item->is_type( 'line_item' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure stock is marked as "reduced" in case payment complete or other stock actions are called.
|
||||
$order->get_data_store()->set_stock_reduced( $order_id, true );
|
||||
// Only reduce stock once for each item.
|
||||
$product = $item->get_product();
|
||||
$item_stock_reduced = $item->get_meta( '_reduced_stock', true );
|
||||
|
||||
do_action( 'woocommerce_reduce_order_stock', $order );
|
||||
if ( $item_stock_reduced || ! $product || ! $product->managing_stock() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$qty = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $order, $item );
|
||||
$item_name = $product->get_formatted_name();
|
||||
$new_stock = wc_update_product_stock( $product, $qty, 'decrease' );
|
||||
|
||||
if ( is_wp_error( $new_stock ) ) {
|
||||
/* translators: %s item name. */
|
||||
$order->add_order_note( sprintf( __( 'Unable to reduce stock for item %s.', 'woocommerce' ), $item_name ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
$item->add_meta_data( '_reduced_stock', $qty, true );
|
||||
$item->save();
|
||||
|
||||
$changes[] = array(
|
||||
'product' => $product,
|
||||
'from' => $new_stock + $qty,
|
||||
'to' => $new_stock,
|
||||
);
|
||||
}
|
||||
|
||||
wc_trigger_stock_change_notifications( $order, $changes );
|
||||
|
||||
do_action( 'woocommerce_reduce_order_stock', $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* After stock change events, triggers emails and adds order notes.
|
||||
*
|
||||
* @since 3.5.0
|
||||
* @param WC_Order $order order object.
|
||||
* @param array $changes Array of changes.
|
||||
*/
|
||||
function wc_trigger_stock_change_notifications( $order, $changes ) {
|
||||
if ( empty( $changes ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order_notes = array();
|
||||
$no_stock_amount = absint( get_option( 'woocommerce_notify_no_stock_amount', 0 ) );
|
||||
$low_stock_amount = absint( get_option( 'woocommerce_notify_low_stock_amount', 2 ) );
|
||||
|
||||
foreach ( $changes as $change ) {
|
||||
$order_notes[] = $change['product']->get_formatted_name() . ' ' . $change['from'] . '→' . $change['to'];
|
||||
|
||||
if ( $change['to'] <= $no_stock_amount ) {
|
||||
do_action( 'woocommerce_no_stock', wc_get_product( $change['product']->get_id() ) );
|
||||
} elseif ( $change['to'] <= $low_stock_amount ) {
|
||||
do_action( 'woocommerce_low_stock', wc_get_product( $change['product']->get_id() ) );
|
||||
}
|
||||
|
||||
if ( $change['to'] < 0 ) {
|
||||
do_action(
|
||||
'woocommerce_product_on_backorder', array(
|
||||
'product' => wc_get_product( $change['product']->get_id() ),
|
||||
'order_id' => $order->get_id(),
|
||||
'quantity' => abs( $change['from'] - $change['to'] ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$order->add_order_note( __( 'Stock levels reduced:', 'woocommerce' ) . ' ' . implode( ', ', $order_notes ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase stock levels for items within an order.
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @param int|WC_Order $order_id Order ID or order instance.
|
||||
*/
|
||||
function wc_increase_stock_levels( $order_id ) {
|
||||
if ( is_a( $order_id, 'WC_Order' ) ) {
|
||||
$order = $order_id;
|
||||
$order_id = $order->get_id();
|
||||
} else {
|
||||
$order = wc_get_order( $order_id );
|
||||
}
|
||||
|
||||
// We need an order, and a store with stock management to continue.
|
||||
if ( ! $order || 'yes' !== get_option( 'woocommerce_manage_stock' ) || ! apply_filters( 'woocommerce_can_restore_order_stock', true, $order ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop over all items.
|
||||
foreach ( $order->get_items() as $item ) {
|
||||
if ( ! $item->is_type( 'line_item' ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only reduce stock once for each item.
|
||||
$product = $item->get_product();
|
||||
$item_stock_reduced = $item->get_meta( '_reduced_stock', true );
|
||||
|
||||
if ( ! $item_stock_reduced || ! $product || ! $product->managing_stock() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item_name = $product->get_formatted_name();
|
||||
$new_stock = wc_update_product_stock( $product, $item_stock_reduced, 'increase' );
|
||||
|
||||
if ( is_wp_error( $new_stock ) ) {
|
||||
/* translators: %s item name. */
|
||||
$order->add_order_note( sprintf( __( 'Unable to restore stock for item %s.', 'woocommerce' ), $item_name ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
$item->delete_meta_data( '_reduced_stock' );
|
||||
$item->save();
|
||||
|
||||
$changes[] = $item_name . ' ' . ( $new_stock - $item_stock_reduced ) . '→' . $new_stock;
|
||||
}
|
||||
|
||||
if ( $changes ) {
|
||||
$order->add_order_note( __( 'Stock levels increased:', 'woocommerce' ) . ' ' . implode( ', ', $changes ) );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_restore_order_stock', $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* See how much stock is being held in pending orders.
|
||||
*
|
||||
* @since 3.5.0
|
||||
* @param WC_Product $product Product to check.
|
||||
* @param integer $exclude_order_id Order ID to exclude.
|
||||
* @return int
|
||||
*/
|
||||
function wc_get_held_stock_quantity( $product, $exclude_order_id = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT SUM( order_item_meta.meta_value ) AS held_qty
|
||||
FROM {$wpdb->posts} AS posts
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_items as order_items ON posts.ID = order_items.order_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta2 ON order_items.order_item_id = order_item_meta2.order_item_id
|
||||
WHERE order_item_meta.meta_key = '_qty'
|
||||
AND order_item_meta2.meta_key = %s
|
||||
AND order_item_meta2.meta_value = %d
|
||||
AND posts.post_type IN ( '" . implode( "','", wc_get_order_types() ) . "' )
|
||||
AND posts.post_status = 'wc-pending'
|
||||
AND posts.ID != %d;",
|
||||
'variation' === get_post_type( $product->get_stock_managed_by_id() ) ? '_variation_id' : '_product_id',
|
||||
$product->get_stock_managed_by_id(),
|
||||
$exclude_order_id
|
||||
)
|
||||
); // WPCS: unprepared SQL ok.
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ if ( ! function_exists( 'wc_create_new_customer' ) ) {
|
|||
$customer_id = wp_insert_user( $new_customer_data );
|
||||
|
||||
if ( is_wp_error( $customer_id ) ) {
|
||||
return new WP_Error( 'registration-error', '<strong>' . __( 'Error:', 'woocommerce' ) . '</strong> ' . __( 'Couldn’t register you… please contact us if you continue to have problems.', 'woocommerce' ) );
|
||||
return new WP_Error( 'registration-error', __( 'Couldn’t register you… please contact us if you continue to have problems.', 'woocommerce' ) );
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_created_customer', $customer_id, $new_customer_data, $password_generated );
|
||||
|
|
|
@ -175,9 +175,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.1.tgz",
|
||||
"integrity": "sha1-s4u4h22ehr7plJVqBOch6IskjrI=",
|
||||
"version": "5.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
|
||||
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"co": "^4.6.0",
|
||||
|
@ -1662,6 +1662,12 @@
|
|||
"electron-to-chromium": "^1.3.47"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
|
||||
"integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==",
|
||||
"dev": true
|
||||
},
|
||||
"builtin-modules": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
|
||||
|
@ -1833,16 +1839,16 @@
|
|||
}
|
||||
},
|
||||
"chromedriver": {
|
||||
"version": "2.38.2",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.38.2.tgz",
|
||||
"integrity": "sha1-5EbpO2vNHytbrlRMemTUcOc6R9E=",
|
||||
"version": "2.40.0",
|
||||
"resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.40.0.tgz",
|
||||
"integrity": "sha512-ewvRQ1HMk0vpFSWYCk5hKDoEz5QMPplx5w3C6/Me+03y1imr67l3Hxl9U0jn3mu2N7+c7BoC7JtNW6HzbRAwDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"del": "^3.0.0",
|
||||
"extract-zip": "^1.6.6",
|
||||
"extract-zip": "^1.6.7",
|
||||
"kew": "^0.7.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"request": "^2.85.0"
|
||||
"request": "^2.87.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"request": {
|
||||
|
@ -2175,9 +2181,9 @@
|
|||
}
|
||||
},
|
||||
"cross-env": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.1.4.tgz",
|
||||
"integrity": "sha1-9hwUKR98xlO7hkVwAuqAoEaZ0CI=",
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.1.6.tgz",
|
||||
"integrity": "sha512-VWTDq+G4v383SzgRS7jsAVWqEWF0aKZpDz1GVjhONvPRgHB1LnxP2sXUVFKbykHkPSnfRKS8YdiDevWFwZmQ9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^5.1.0",
|
||||
|
@ -2677,24 +2683,27 @@
|
|||
}
|
||||
},
|
||||
"extract-zip": {
|
||||
"version": "1.6.6",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz",
|
||||
"integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=",
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz",
|
||||
"integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"concat-stream": "1.6.0",
|
||||
"concat-stream": "1.6.2",
|
||||
"debug": "2.6.9",
|
||||
"mkdirp": "0.5.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"yauzl": "2.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
|
||||
"integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
|
||||
"concat-stream": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^2.2.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2706,9 +2715,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
|
||||
"integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
|
||||
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-glob": {
|
||||
|
@ -3266,14 +3275,25 @@
|
|||
"dev": true
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
|
||||
"integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
|
||||
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.5",
|
||||
"combined-stream": "1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"combined-stream": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
|
||||
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fragment-cache": {
|
||||
|
@ -6650,7 +6670,7 @@
|
|||
"p-map": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
|
||||
"integrity": "sha1-5OlPMR6rvIYzoeeZCBZfyiYkG2s=",
|
||||
"integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
|
||||
"dev": true
|
||||
},
|
||||
"p-try": {
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
"babel-preset-stage-2": "^6.13.0",
|
||||
"chai": "^4.1.2",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chromedriver": "^2.37.0",
|
||||
"chromedriver": "^2.40.0",
|
||||
"config": "^1.24.0",
|
||||
"cross-env": "~5.1.1",
|
||||
"cross-env": "^5.1.6",
|
||||
"grunt": "^1.0.3",
|
||||
"grunt-checktextdomain": "~1.0.1",
|
||||
"grunt-contrib-clean": "~1.1.0",
|
||||
|
|
|
@ -7,12 +7,17 @@
|
|||
*/
|
||||
class WC_Helper_Coupon {
|
||||
|
||||
protected static $custom_types = array();
|
||||
|
||||
/**
|
||||
* Create a dummy coupon.
|
||||
*
|
||||
* @param string $coupon_code
|
||||
* @param array $meta
|
||||
*
|
||||
* @return WC_Coupon
|
||||
*/
|
||||
public static function create_coupon( $coupon_code = 'dummycoupon' ) {
|
||||
public static function create_coupon( $coupon_code = 'dummycoupon', $meta = array() ) {
|
||||
// Insert post
|
||||
$coupon_id = wp_insert_post( array(
|
||||
'post_title' => $coupon_code,
|
||||
|
@ -21,24 +26,30 @@ class WC_Helper_Coupon {
|
|||
'post_excerpt' => 'This is a dummy coupon',
|
||||
) );
|
||||
|
||||
// Update meta
|
||||
update_post_meta( $coupon_id, 'discount_type', 'fixed_cart' );
|
||||
update_post_meta( $coupon_id, 'coupon_amount', '1' );
|
||||
update_post_meta( $coupon_id, 'individual_use', 'no' );
|
||||
update_post_meta( $coupon_id, 'product_ids', '' );
|
||||
update_post_meta( $coupon_id, 'exclude_product_ids', '' );
|
||||
update_post_meta( $coupon_id, 'usage_limit', '' );
|
||||
update_post_meta( $coupon_id, 'usage_limit_per_user', '' );
|
||||
update_post_meta( $coupon_id, 'limit_usage_to_x_items', '' );
|
||||
update_post_meta( $coupon_id, 'expiry_date', '' );
|
||||
update_post_meta( $coupon_id, 'free_shipping', 'no' );
|
||||
update_post_meta( $coupon_id, 'exclude_sale_items', 'no' );
|
||||
update_post_meta( $coupon_id, 'product_categories', array() );
|
||||
update_post_meta( $coupon_id, 'exclude_product_categories', array() );
|
||||
update_post_meta( $coupon_id, 'minimum_amount', '' );
|
||||
update_post_meta( $coupon_id, 'maximum_amount', '' );
|
||||
update_post_meta( $coupon_id, 'customer_email', array() );
|
||||
update_post_meta( $coupon_id, 'usage_count', '0' );
|
||||
$meta = wp_parse_args( $meta, array(
|
||||
'discount_type' => 'fixed_cart',
|
||||
'coupon_amount' => '1',
|
||||
'individual_use' => 'no',
|
||||
'product_ids' => '',
|
||||
'exclude_product_ids' => '',
|
||||
'usage_limit' => '',
|
||||
'usage_limit_per_user' => '',
|
||||
'limit_usage_to_x_items' => '',
|
||||
'expiry_date' => '',
|
||||
'free_shipping' => 'no',
|
||||
'exclude_sale_items' => 'no',
|
||||
'product_categories' => array(),
|
||||
'exclude_product_categories' => array(),
|
||||
'minimum_amount' => '',
|
||||
'maximum_amount' => '',
|
||||
'customer_email' => array(),
|
||||
'usage_count' => '0',
|
||||
) );
|
||||
|
||||
// Update meta.
|
||||
foreach ( $meta as $key => $value ) {
|
||||
update_post_meta( $coupon_id, $key, $value );
|
||||
}
|
||||
|
||||
return new WC_Coupon( $coupon_code );
|
||||
}
|
||||
|
@ -52,6 +63,71 @@ class WC_Helper_Coupon {
|
|||
*/
|
||||
public static function delete_coupon( $coupon_id ) {
|
||||
wp_delete_post( $coupon_id, true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom coupon type.
|
||||
*
|
||||
* @param string $coupon_type
|
||||
*/
|
||||
public static function register_custom_type( $coupon_type ) {
|
||||
static $filters_added = false;
|
||||
if ( isset( self::$custom_types[ $coupon_type ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
self::$custom_types[ $coupon_type ] = "Testing custom type {$coupon_type}";
|
||||
|
||||
if ( ! $filters_added ) {
|
||||
add_filter( 'woocommerce_coupon_discount_types', array( __CLASS__, 'filter_discount_types' ) );
|
||||
add_filter( 'woocommerce_coupon_get_discount_amount', array( __CLASS__, 'filter_get_discount_amount' ), 10, 5 );
|
||||
add_filter( 'woocommerce_coupon_is_valid_for_product', '__return_true' );
|
||||
$filters_added = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister custom coupon type.
|
||||
*
|
||||
* @param $coupon_type
|
||||
*/
|
||||
public static function unregister_custom_type( $coupon_type ) {
|
||||
unset( self::$custom_types[ $coupon_type ] );
|
||||
if ( empty( self::$custom_types ) ) {
|
||||
remove_filter( 'woocommerce_coupon_discount_types', array( __CLASS__, 'filter_discount_types' ) );
|
||||
remove_filter( 'woocommerce_coupon_get_discount_amount', array( __CLASS__, 'filter_get_discount_amount' ) );
|
||||
remove_filter( 'woocommerce_coupon_is_valid_for_product', '__return_true' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom discount types.
|
||||
*
|
||||
* @param array $discount_types
|
||||
* @return array
|
||||
*/
|
||||
public static function filter_discount_types( $discount_types ) {
|
||||
return array_merge( $discount_types, self::$custom_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom discount type amount. Works like 'percent' type.
|
||||
*
|
||||
* @param float $discount
|
||||
* @param float $discounting_amount
|
||||
* @param array|null $item
|
||||
* @param bool $single
|
||||
* @param WC_Coupon $coupon
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function filter_get_discount_amount( $discount, $discounting_amount, $item, $single, $coupon ) {
|
||||
if ( ! isset( self::$custom_types [ $coupon->get_discount_type() ] ) ) {
|
||||
return $discount;
|
||||
}
|
||||
|
||||
return (float) $coupon->get_amount() * ( $discounting_amount / 100 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
/**
|
||||
* Class Coupon.
|
||||
* @package WooCommerce\Tests\Coupon
|
||||
* @group coupons
|
||||
*/
|
||||
class WC_Tests_Coupon extends WC_Unit_Test_Case {
|
||||
|
||||
|
@ -315,4 +316,79 @@ class WC_Tests_Coupon extends WC_Unit_Test_Case {
|
|||
$this->assertFalse( $expired_coupon->is_valid() );
|
||||
$this->assertEquals( $expired_coupon->get_error_message(), $expired_coupon->get_coupon_error( WC_Coupon::E_WC_COUPON_EXPIRED ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test an item limit for percent discounts.
|
||||
*/
|
||||
public function test_percent_discount_item_limit() {
|
||||
// Create product
|
||||
$product = WC_Helper_Product::create_simple_product();
|
||||
update_post_meta( $product->get_id(), '_price', '10' );
|
||||
update_post_meta( $product->get_id(), '_regular_price', '10' );
|
||||
|
||||
// Create coupon
|
||||
$coupon = WC_Helper_Coupon::create_coupon( 'dummycoupon', array(
|
||||
'discount_type' => 'percent',
|
||||
'coupon_amount' => '5',
|
||||
'limit_usage_to_x_items' => 1,
|
||||
) );
|
||||
|
||||
// We need this to have the calculate_totals() method calculate totals.
|
||||
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
|
||||
define( 'WOOCOMMERCE_CHECKOUT', true );
|
||||
}
|
||||
|
||||
// Add 2 products and coupon to cart.
|
||||
WC()->cart->add_to_cart( $product->get_id(), 2 );
|
||||
WC()->cart->add_discount( $coupon->get_code() );
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
// Test if the cart total amount is equal 19.5 (coupon only applying to one item).
|
||||
$this->assertEquals( 19.5, WC()->cart->total );
|
||||
|
||||
// Clean up
|
||||
wc_clear_notices();
|
||||
WC()->cart->empty_cart();
|
||||
WC()->cart->remove_coupons();
|
||||
WC_Helper_Coupon::delete_coupon( $coupon->get_id() );
|
||||
WC_Helper_Product::delete_product( $product->get_id() );
|
||||
}
|
||||
|
||||
public function test_custom_discount_item_limit() {
|
||||
// Register custom discount type.
|
||||
WC_Helper_Coupon::register_custom_type( __FUNCTION__ );
|
||||
|
||||
// Create product
|
||||
$product = WC_Helper_Product::create_simple_product();
|
||||
update_post_meta( $product->get_id(), '_price', '10' );
|
||||
update_post_meta( $product->get_id(), '_regular_price', '10' );
|
||||
|
||||
// Create coupon
|
||||
$coupon = WC_Helper_Coupon::create_coupon( 'dummycoupon', array(
|
||||
'discount_type' => __FUNCTION__,
|
||||
'coupon_amount' => '5',
|
||||
'limit_usage_to_x_items' => 1,
|
||||
) );
|
||||
|
||||
// We need this to have the calculate_totals() method calculate totals.
|
||||
if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) {
|
||||
define( 'WOOCOMMERCE_CHECKOUT', true );
|
||||
}
|
||||
|
||||
// Add 4 products and coupon to cart.
|
||||
WC()->cart->add_to_cart( $product->get_id(), 4 );
|
||||
WC()->cart->add_discount( $coupon->get_code() );
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
// Test if the cart total amount is equal 39.5 (coupon only applying to one item).
|
||||
$this->assertEquals( 39.5, WC()->cart->total );
|
||||
|
||||
// Clean up
|
||||
wc_clear_notices();
|
||||
WC()->cart->empty_cart();
|
||||
WC()->cart->remove_coupons();
|
||||
WC_Helper_Coupon::delete_coupon( $coupon->get_id() );
|
||||
WC_Helper_Product::delete_product( $product->get_id() );
|
||||
WC_Helper_Coupon::unregister_custom_type( __FUNCTION__ );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class WC_Tests_WC_Product_Query extends WC_Unit_Test_Case {
|
|||
$product1->set_sale_price( '5.00' );
|
||||
$product1->save();
|
||||
|
||||
$product2 = new WC_Product_Grouped();
|
||||
$product2 = new WC_Product_Simple();
|
||||
$product2->set_sku( 'sku2' );
|
||||
$product2->set_regular_price( '12.50' );
|
||||
$product2->set_sale_price( '5.00' );
|
||||
|
|
Loading…
Reference in New Issue