PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
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 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
← All changes | includes/Services/Preview_Receipt_Builder.php +152 -175 1.10.9 → 1.10.20 View file →
@@ -498,18 +498,48 @@
498 498 ),
499 499 );
500 500
501 501 /**
502 - * Build a preview receipt payload.
502 + * Build a preview receipt payload, filtered like a live receipt.
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 + *
504 533 * Returns an array matching the receipt data schema with all sections
505 - * populated using the store's real settings, products, and tax rates.
534 + * populated using the store's real settings, products, and tax rates. The
535 + * gallery fixture loader layers its overrides on this before filtering.
506 536 *
507 537 * @param object|null $pos_store POS store object. Falls back to default store.
508 538 *
509 539 * @return array Complete receipt data array.
510 540 */
511 - public function build( $pos_store = null ): array {
541 + public function sample( $pos_store = null ): array {
512 542 $resolved_store = null === $pos_store ? wcpos_get_store() : $pos_store;
513 543 if ( ! \is_object( $resolved_store ) ) {
514 544 $resolved_store = wcpos_get_store();
515 545 }
@@ -535,9 +565,9 @@
535 565 );
536 566 $dp = $this->store_resolver->resolve_price_num_decimals();
537 567
538 568 // Build line items.
539 - $lines = array();
569 + $priced_lines = array();
540 570 $lines_total_excl = 0.0;
541 571 $lines_total_incl = 0.0;
542 572
543 573 foreach ( $raw_products as $index => $product ) {
@@ -569,54 +599,51 @@
569 599 $line_regular_excl = round( $regular_unit_excl * $qty, $dp );
570 600 $line_savings_incl = round( $unit_savings_incl * $qty, $dp );
571 601 $line_savings_excl = round( $unit_savings_excl * $qty, $dp );
572 602
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,
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 + ),
599 641 'savings_in_discounts' => false,
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(),
642 + 'total_refunded' => 0.0,
643 + 'taxes' => array(),
644 + 'meta' => $product['meta'] ?? array(),
645 + 'attributes' => $product['attributes'] ?? array(),
619 646 );
620 647
621 648 $lines_total_excl += $line_total_excl;
622 649 $lines_total_incl += $line_total_incl;
@@ -651,52 +678,47 @@
651 678
652 679 // Distribute discount proportionally across line items with remainder correction.
653 680 $sum_discount_excl = 0.0;
654 681 $sum_discount_incl = 0.0;
655 - $last_index = count( $lines ) - 1;
682 + $last_index = count( $priced_lines ) - 1;
656 683
657 - foreach ( $lines as $i => &$line ) {
684 + foreach ( $priced_lines as $i => &$line ) {
658 685 if ( $lines_total_excl > 0 ) {
659 - $share = $line['line_subtotal_excl'] / $lines_total_excl;
686 + $share = $line['line_subtotal']['excl'] / $lines_total_excl;
660 687 } else {
661 - $share = 1.0 / count( $lines );
688 + $share = 1.0 / count( $priced_lines );
662 689 }
663 690
664 691 if ( $i < $last_index ) {
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'];
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'];
669 696 } else {
670 697 // Assign remainder to last item so distributed totals match exactly.
671 - $line['discounts_excl'] = round( $discount_excl - $sum_discount_excl, $dp );
672 - $line['discounts_incl'] = round( $discount_incl - $sum_discount_incl, $dp );
698 + $line['discounts']['excl'] = round( $discount_excl - $sum_discount_excl, $dp );
699 + $line['discounts']['incl'] = round( $discount_incl - $sum_discount_incl, $dp );
673 700 }
674 701
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'];
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 );
682 706 }
683 707 unset( $line );
684 708
685 - // Totals.
686 - $subtotal_excl = $lines_total_excl;
687 - $subtotal_incl = $lines_total_incl;
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 + );
688 715
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 -
694 716 // Taxable base: line items - discount + shipping + fee (all excl).
695 - $taxable_excl = $subtotal_excl - $discount_excl + $shipping_excl + $fee_excl;
717 + $taxable_excl = $lines_total_excl - $discount_excl + $shipping_excl + $fee_excl;
696 718 $total_tax = round( $taxable_excl * $tax_rate / 100, $dp );
697 719
698 - $total_excl = $subtotal_excl - $discount_excl + $shipping_excl + $fee_excl;
720 + $total_excl = $lines_total_excl - $discount_excl + $shipping_excl + $fee_excl;
699 721 $total_incl = $total_excl + $total_tax;
700 722
701 723 // Payment: cash rounded up to nearest 5.
702 724 $tendered = (float) ( ceil( $total_incl / 5 ) * 5 );
@@ -730,98 +752,72 @@
730 752
731 753 $customer = $this->get_customer();
732 754
733 755 $fees = array(
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(),
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
741 765 ),
742 766 );
743 -
744 767 $shipping = array(
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(),
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
753 778 ),
754 779 );
755 -
756 780 $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
790 + ),
791 + );
792 + $totals = Receipt_Sections::totals(
793 + $lines,
757 794 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,
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,
764 804 ),
805 + $display_incl
765 806 );
766 807
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;
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();
771 811
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,
796 - );
797 -
798 - $taxable_amount_incl = $taxable_excl + $total_tax;
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 -
816 812 $payments = array(
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,
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
824 820 ),
825 821 );
826 822
827 823 $refunds = array();
@@ -1091,19 +1087,10 @@
1091 1087 ),
1092 1088 );
1093 1089
1094 1090 $tax_ids = $samples[ $country ] ?? array();
1095 - $labels = Receipt_I18n_Labels::get_labels( $locale );
1096 1091
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 - );
1092 + return Receipt_Sections::label_tax_ids( $tax_ids, 'customer', $locale );
1106 1093 }
1107 1094
1108 1095 /**
1109 1096 * Get product data for line items.
@@ -1136,19 +1123,9 @@
1136 1123 if ( ! empty( $children ) ) {
1137 1124 $variation = wc_get_product( $children[0] );
1138 1125 if ( $variation instanceof \WC_Product_Variation ) {
1139 1126 $product = $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 - }
1127 + $meta = Receipt_Sections::variation_attribute_pairs( $variation );
1151 1128 }
1152 1129 }
1153 1130 }
1154 1131