PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.17
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.17
1.10.25 1.10.24 1.10.23 1.10.22 1.10.21 1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 All 169 releases
← All changes | includes/Services/Preview_Receipt_Builder.php +175 -152 1.10.25 → 1.10.17 View file →
@@ -498,48 +498,18 @@
498 498 ),
499 499 );
500 500
501 501 /**
502 - * Build a preview receipt payload, filtered like a live receipt.
502 + * Build a preview receipt payload.
503 503 *
504 - * @param object|null $pos_store POS store object. Falls back to default store.
505 - *
506 - * @return array Complete receipt data array.
507 - */
508 - public function build( $pos_store = null ): array {
509 - return self::apply_receipt_data_filter( $this->sample( $pos_store ) );
510 - }
511 -
512 - /**
513 - * Run a sample payload through the receipt data filter.
514 - *
515 - * Same extension point as live receipts, so plugin-added keys show in the
516 - * editor and gallery. The unsaved order (id 0) keeps plugins written against
517 - * WC_Order from fataling and lets them tell a sample apart. Each sample path
518 - * calls this exactly once, after every override has been applied: the gallery
519 - * fixture loader replaces whole row lists, so filtering before it would drop
520 - * keys a plugin put on a row.
521 - *
522 - * @param array $data Assembled sample payload.
523 - *
524 - * @return array Filtered payload.
525 - */
526 - public static function apply_receipt_data_filter( array $data ): array {
527 - return (array) apply_filters( 'woocommerce_pos_receipt_data', $data, new \WC_Order(), 'preview' );
528 - }
529 -
530 - /**
531 - * Assemble the unfiltered sample payload.
532 - *
533 504 * Returns an array matching the receipt data schema with all sections
534 - * populated using the store's real settings, products, and tax rates. The
535 - * gallery fixture loader layers its overrides on this before filtering.
505 + * populated using the store's real settings, products, and tax rates.
536 506 *
537 507 * @param object|null $pos_store POS store object. Falls back to default store.
538 508 *
539 509 * @return array Complete receipt data array.
540 510 */
541 - public function sample( $pos_store = null ): array {
511 + public function build( $pos_store = null ): array {
542 512 $resolved_store = null === $pos_store ? wcpos_get_store() : $pos_store;
543 513 if ( ! \is_object( $resolved_store ) ) {
544 514 $resolved_store = wcpos_get_store();
545 515 }
@@ -565,9 +535,9 @@
565 535 );
566 536 $dp = $this->store_resolver->resolve_price_num_decimals();
567 537
568 538 // Build line items.
569 - $priced_lines = array();
539 + $lines = array();
570 540 $lines_total_excl = 0.0;
571 541 $lines_total_incl = 0.0;
572 542
573 543 foreach ( $raw_products as $index => $product ) {
@@ -599,51 +569,54 @@
599 569 $line_regular_excl = round( $regular_unit_excl * $qty, $dp );
600 570 $line_savings_incl = round( $unit_savings_incl * $qty, $dp );
601 571 $line_savings_excl = round( $unit_savings_excl * $qty, $dp );
602 572
603 - $priced_lines[] = array(
604 - 'key' => (string) ( $index + 1 ),
605 - 'sku' => $product['sku'],
606 - 'name' => $product['name'],
607 - 'qty' => (float) $qty,
608 - 'qty_refunded' => 0.0,
609 - 'regular_price' => array(
610 - 'incl' => $regular_unit_incl,
611 - 'excl' => $regular_unit_excl,
612 - ),
613 - 'selling_price' => array(
614 - 'incl' => $selling_unit_incl,
615 - 'excl' => $selling_unit_excl,
616 - ),
617 - 'unit_savings' => array(
618 - 'incl' => $unit_savings_incl,
619 - 'excl' => $unit_savings_excl,
620 - ),
621 - 'line_regular_total' => array(
622 - 'incl' => $line_regular_incl,
623 - 'excl' => $line_regular_excl,
624 - ),
625 - 'line_selling_total' => array(
626 - 'incl' => $line_total_incl,
627 - 'excl' => $line_total_excl,
628 - ),
629 - 'line_savings' => array(
630 - 'incl' => $line_savings_incl,
631 - 'excl' => $line_savings_excl,
632 - ),
633 - 'unit_subtotal' => array(
634 - 'incl' => $selling_unit_incl,
635 - 'excl' => $selling_unit_excl,
636 - ),
637 - 'line_subtotal' => array(
638 - 'incl' => $line_total_incl,
639 - 'excl' => $line_total_excl,
640 - ),
573 + $unit_price_rounded = $display_incl ? $selling_unit_incl : $selling_unit_excl;
574 +
575 + $lines[] = array(
576 + 'key' => (string) ( $index + 1 ),
577 + 'sku' => $product['sku'],
578 + 'name' => $product['name'],
579 + 'qty' => (float) $qty,
580 + 'qty_refunded' => 0.0,
581 + 'regular_price' => $display_incl ? $regular_unit_incl : $regular_unit_excl,
582 + 'regular_price_incl' => $regular_unit_incl,
583 + 'regular_price_excl' => $regular_unit_excl,
584 + 'selling_price' => $unit_price_rounded,
585 + 'selling_price_incl' => $selling_unit_incl,
586 + 'selling_price_excl' => $selling_unit_excl,
587 + 'unit_savings' => $display_incl ? $unit_savings_incl : $unit_savings_excl,
588 + 'unit_savings_incl' => $unit_savings_incl,
589 + 'unit_savings_excl' => $unit_savings_excl,
590 + 'line_regular_total' => $display_incl ? $line_regular_incl : $line_regular_excl,
591 + 'line_regular_total_incl' => $line_regular_incl,
592 + 'line_regular_total_excl' => $line_regular_excl,
593 + 'line_selling_total' => $display_incl ? $line_total_incl : $line_total_excl,
594 + 'line_selling_total_incl' => $line_total_incl,
595 + 'line_selling_total_excl' => $line_total_excl,
596 + 'line_savings' => $display_incl ? $line_savings_incl : $line_savings_excl,
597 + 'line_savings_incl' => $line_savings_incl,
598 + 'line_savings_excl' => $line_savings_excl,
641 599 'savings_in_discounts' => false,
642 - 'total_refunded' => 0.0,
643 - 'taxes' => array(),
644 - 'meta' => $product['meta'] ?? array(),
645 - 'attributes' => $product['attributes'] ?? array(),
600 + 'unit_subtotal' => $unit_price_rounded,
601 + 'unit_subtotal_incl' => $selling_unit_incl,
602 + 'unit_subtotal_excl' => $selling_unit_excl,
603 + 'unit_price' => $unit_price_rounded,
604 + 'unit_price_incl' => $selling_unit_incl,
605 + 'unit_price_excl' => $selling_unit_excl,
606 + 'line_subtotal' => $display_incl ? $line_total_incl : $line_total_excl,
607 + 'line_subtotal_incl' => $line_total_incl,
608 + 'line_subtotal_excl' => $line_total_excl,
609 + 'discounts' => 0.0,
610 + 'discounts_incl' => 0.0,
611 + 'discounts_excl' => 0.0,
612 + 'line_total' => $display_incl ? $line_total_incl : $line_total_excl,
613 + 'line_total_incl' => $line_total_incl,
614 + 'line_total_excl' => $line_total_excl,
615 + 'total_refunded' => 0.0,
616 + 'taxes' => array(),
617 + 'meta' => $product['meta'] ?? array(),
618 + 'attributes' => $product['attributes'] ?? array(),
646 619 );
647 620
648 621 $lines_total_excl += $line_total_excl;
649 622 $lines_total_incl += $line_total_incl;
@@ -678,47 +651,52 @@
678 651
679 652 // Distribute discount proportionally across line items with remainder correction.
680 653 $sum_discount_excl = 0.0;
681 654 $sum_discount_incl = 0.0;
682 - $last_index = count( $priced_lines ) - 1;
655 + $last_index = count( $lines ) - 1;
683 656
684 - foreach ( $priced_lines as $i => &$line ) {
657 + foreach ( $lines as $i => &$line ) {
685 658 if ( $lines_total_excl > 0 ) {
686 - $share = $line['line_subtotal']['excl'] / $lines_total_excl;
659 + $share = $line['line_subtotal_excl'] / $lines_total_excl;
687 660 } else {
688 - $share = 1.0 / count( $priced_lines );
661 + $share = 1.0 / count( $lines );
689 662 }
690 663
691 664 if ( $i < $last_index ) {
692 - $line['discounts']['excl'] = round( $discount_excl * $share, $dp );
693 - $line['discounts']['incl'] = round( $discount_incl * $share, $dp );
694 - $sum_discount_excl += $line['discounts']['excl'];
695 - $sum_discount_incl += $line['discounts']['incl'];
665 + $line['discounts_excl'] = round( $discount_excl * $share, $dp );
666 + $line['discounts_incl'] = round( $discount_incl * $share, $dp );
667 + $sum_discount_excl += $line['discounts_excl'];
668 + $sum_discount_incl += $line['discounts_incl'];
696 669 } else {
697 670 // Assign remainder to last item so distributed totals match exactly.
698 - $line['discounts']['excl'] = round( $discount_excl - $sum_discount_excl, $dp );
699 - $line['discounts']['incl'] = round( $discount_incl - $sum_discount_incl, $dp );
671 + $line['discounts_excl'] = round( $discount_excl - $sum_discount_excl, $dp );
672 + $line['discounts_incl'] = round( $discount_incl - $sum_discount_incl, $dp );
700 673 }
701 674
702 - $line['line_total']['excl'] = round( $line['line_subtotal']['excl'] - $line['discounts']['excl'], $dp );
703 - $line['line_total']['incl'] = round( $line['line_subtotal']['incl'] - $line['discounts']['incl'], $dp );
704 - $line['unit_price']['incl'] = round( $line['line_total']['incl'] / $line['qty'], $dp );
705 - $line['unit_price']['excl'] = round( $line['line_total']['excl'] / $line['qty'], $dp );
675 + $line['discounts'] = $display_incl ? $line['discounts_incl'] : $line['discounts_excl'];
676 + $line['line_total_excl'] = round( $line['line_subtotal_excl'] - $line['discounts_excl'], $dp );
677 + $line['line_total_incl'] = round( $line['line_subtotal_incl'] - $line['discounts_incl'], $dp );
678 + $line['line_total'] = $display_incl ? $line['line_total_incl'] : $line['line_total_excl'];
679 + $line['unit_price_incl'] = round( $line['line_total_incl'] / $line['qty'], $dp );
680 + $line['unit_price_excl'] = round( $line['line_total_excl'] / $line['qty'], $dp );
681 + $line['unit_price'] = $display_incl ? $line['unit_price_incl'] : $line['unit_price_excl'];
706 682 }
707 683 unset( $line );
708 684
709 - $lines = array_map(
710 - static function ( array $line ) use ( $display_incl ): array {
711 - return Receipt_Sections::line( $line, $display_incl );
712 - },
713 - $priced_lines
714 - );
685 + // Totals.
686 + $subtotal_excl = $lines_total_excl;
687 + $subtotal_incl = $lines_total_incl;
715 688
689 + // Item count summaries — matches the real builder's totals.total_qty
690 + // / totals.line_count so preview mirrors the live receipt schema.
691 + $total_qty = (float) array_sum( array_column( $lines, 'qty' ) );
692 + $line_count = \count( $lines );
693 +
716 694 // Taxable base: line items - discount + shipping + fee (all excl).
717 - $taxable_excl = $lines_total_excl - $discount_excl + $shipping_excl + $fee_excl;
695 + $taxable_excl = $subtotal_excl - $discount_excl + $shipping_excl + $fee_excl;
718 696 $total_tax = round( $taxable_excl * $tax_rate / 100, $dp );
719 697
720 - $total_excl = $lines_total_excl - $discount_excl + $shipping_excl + $fee_excl;
698 + $total_excl = $subtotal_excl - $discount_excl + $shipping_excl + $fee_excl;
721 699 $total_incl = $total_excl + $total_tax;
722 700
723 701 // Payment: cash rounded up to nearest 5.
724 702 $tendered = (float) ( ceil( $total_incl / 5 ) * 5 );
@@ -752,72 +730,98 @@
752 730
753 731 $customer = $this->get_customer();
754 732
755 733 $fees = array(
756 - Receipt_Sections::fee(
757 - $fee_label,
758 - array(
759 - 'incl' => $fee_incl,
760 - 'excl' => $fee_excl,
761 - ),
762 - array(),
763 - array(),
764 - $display_incl
734 + array(
735 + 'label' => $fee_label,
736 + 'total' => $display_incl ? $fee_incl : $fee_excl,
737 + 'total_incl' => $fee_incl,
738 + 'total_excl' => $fee_excl,
739 + 'taxes' => array(),
740 + 'meta' => array(),
765 741 ),
766 742 );
743 +
767 744 $shipping = array(
768 - Receipt_Sections::shipping(
769 - $shipping_label,
770 - 'flat_rate',
771 - array(
772 - 'incl' => $shipping_incl,
773 - 'excl' => $shipping_excl,
774 - ),
775 - array(),
776 - array(),
777 - $display_incl
745 + array(
746 + 'label' => $shipping_label,
747 + 'method_id' => 'flat_rate',
748 + 'total' => $display_incl ? $shipping_incl : $shipping_excl,
749 + 'total_incl' => $shipping_incl,
750 + 'total_excl' => $shipping_excl,
751 + 'taxes' => array(),
752 + 'meta' => array(),
778 753 ),
779 754 );
755 +
780 756 $discounts = array(
781 - Receipt_Sections::discount(
782 - $discount_label,
783 - 'SUMMER10',
784 - 'fixed_cart',
785 - array(
786 - 'incl' => $discount_incl,
787 - 'excl' => $discount_excl,
788 - ),
789 - $display_incl
757 + array(
758 + 'label' => $discount_label,
759 + 'code' => 'SUMMER10',
760 + 'discount_type' => 'fixed_cart',
761 + 'total' => $display_incl ? $discount_incl : $discount_excl,
762 + 'total_incl' => $discount_incl,
763 + 'total_excl' => $discount_excl,
790 764 ),
791 765 );
792 - $totals = Receipt_Sections::totals(
793 - $lines,
794 - array(
795 - 'discount_total' => array(
796 - 'incl' => $discount_incl,
797 - 'excl' => $discount_excl,
798 - ),
799 - 'tax_total' => $total_tax,
800 - 'total' => $total_incl,
801 - 'paid_total' => $total_incl,
802 - 'change_total' => $change_total,
803 - 'refund_total' => 0.0,
804 - ),
805 - $display_incl
766 +
767 + $sale_savings_total_incl = (float) array_sum( array_column( $lines, 'line_savings_incl' ) );
768 + $sale_savings_total_excl = (float) array_sum( array_column( $lines, 'line_savings_excl' ) );
769 + $total_saved_incl = $discount_incl + $sale_savings_total_incl;
770 + $total_saved_excl = $discount_excl + $sale_savings_total_excl;
771 +
772 + $totals = array(
773 + 'subtotal' => $display_incl ? $subtotal_incl : $subtotal_excl,
774 + 'subtotal_incl' => $subtotal_incl,
775 + 'subtotal_excl' => $subtotal_excl,
776 + 'discount_total' => $display_incl ? $discount_incl : $discount_excl,
777 + 'discount_total_incl' => $discount_incl,
778 + 'discount_total_excl' => $discount_excl,
779 + 'sale_savings_total' => $display_incl ? $sale_savings_total_incl : $sale_savings_total_excl,
780 + 'sale_savings_total_incl' => $sale_savings_total_incl,
781 + 'sale_savings_total_excl' => $sale_savings_total_excl,
782 + 'total_saved' => $display_incl ? $total_saved_incl : $total_saved_excl,
783 + 'total_saved_incl' => $total_saved_incl,
784 + 'total_saved_excl' => $total_saved_excl,
785 + 'total_saved_complete' => true,
786 + 'tax_total' => $total_tax,
787 + 'total' => $display_incl ? $total_incl : $total_excl,
788 + 'total_incl' => $total_incl,
789 + 'total_excl' => $total_excl,
790 + 'paid_total' => $total_incl,
791 + 'change_total' => $change_total,
792 + 'refund_total' => 0.0,
793 + 'net_total' => 0.0,
794 + 'total_qty' => $total_qty,
795 + 'line_count' => $line_count,
806 796 );
807 797
808 - $tax_summary = $tax_rate > 0 ? array(
809 - Receipt_Sections::tax_summary_entry( $tax_code, $tax_rate, $tax_label, false, $taxable_excl, $total_tax ),
810 - ) : array();
798 + $taxable_amount_incl = $taxable_excl + $total_tax;
811 799
800 + if ( $tax_rate > 0 ) {
801 + $tax_summary = array(
802 + array(
803 + 'code' => $tax_code,
804 + 'rate' => $tax_rate,
805 + 'label' => $tax_label,
806 + 'compound' => false,
807 + 'taxable_amount_excl' => $taxable_excl,
808 + 'tax_amount' => $total_tax,
809 + 'taxable_amount_incl' => $taxable_amount_incl,
810 + ),
811 + );
812 + } else {
813 + $tax_summary = array();
814 + }
815 +
812 816 $payments = array(
813 - Receipt_Sections::payment(
814 - 'pos_cash',
815 - /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Cash', 'woocommerce-pos' ),
816 - $total_incl,
817 - '',
818 - $tendered,
819 - $change_total
817 + array(
818 + 'method_id' => 'pos_cash',
819 + 'method_title' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Cash', 'woocommerce-pos' ),
820 + 'amount' => $total_incl,
821 + 'transaction_id' => '',
822 + 'tendered' => $tendered,
823 + 'change' => $change_total,
820 824 ),
821 825 );
822 826
823 827 $refunds = array();
@@ -1087,10 +1091,19 @@
1087 1091 ),
1088 1092 );
1089 1093
1090 1094 $tax_ids = $samples[ $country ] ?? array();
1095 + $labels = Receipt_I18n_Labels::get_labels( $locale );
1091 1096
1092 - return Receipt_Sections::label_tax_ids( $tax_ids, 'customer', $locale );
1097 + return array_map(
1098 + static function ( array $tax_id ) use ( $labels ): array {
1099 + $type = (string) $tax_id['type'];
1100 + $tax_id['label'] = $labels[ 'customer_tax_id_label_' . $type ] ?? $labels['customer_tax_id_label_other'];
1101 +
1102 + return $tax_id;
1103 + },
1104 + $tax_ids
1105 + );
1093 1106 }
1094 1107
1095 1108 /**
1096 1109 * Get product data for line items.
@@ -1123,9 +1136,19 @@
1123 1136 if ( ! empty( $children ) ) {
1124 1137 $variation = wc_get_product( $children[0] );
1125 1138 if ( $variation instanceof \WC_Product_Variation ) {
1126 1139 $product = $variation;
1127 - $meta = Receipt_Sections::variation_attribute_pairs( $variation );
1140 + foreach ( $variation->get_variation_attributes() as $attr_key => $attr_value ) {
1141 + if ( '' !== $attr_value ) {
1142 + $taxonomy = str_replace( 'attribute_', '', $attr_key );
1143 + $label = wc_attribute_label( $taxonomy, $variation );
1144 + $value = $variation->get_attribute( $taxonomy );
1145 + $meta[] = array(
1146 + 'key' => wp_strip_all_tags( $label ),
1147 + 'value' => wp_strip_all_tags( '' !== $value ? $value : $attr_value ),
1148 + );
1149 + }
1150 + }
1128 1151 }
1129 1152 }
1130 1153 }
1131 1154