← All changes
|
includes/Services/Preview_Receipt_Builder.php
+216
-299
1.9.15
→
1.10.20
View file →
| @@ -21,15 +21,8 @@ | ||
| 21 | 21 | */ |
| 22 | 22 | class Preview_Receipt_Builder { |
| 23 | 23 | |
| 24 | 24 | /** |
| 25 | - * POS store object used to populate store fields. | |
| 26 | - * | |
| 27 | - * @var object | |
| 28 | - */ | |
| 29 | - private $pos_store; | |
| 30 | - | |
| 31 | - /** | |
| 32 | 25 | * Shared store resolver. |
| 33 | 26 | * |
| 34 | 27 | * @var Receipt_Store_Resolver |
| 35 | 28 | */ |
| @@ -505,24 +498,53 @@ | ||
| 505 | 498 | ), |
| 506 | 499 | ); |
| 507 | 500 | |
| 508 | 501 | /** |
| 509 | - * Build a preview receipt payload. | |
| 502 | + * Build a preview receipt payload, filtered like a live receipt. | |
| 510 | 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 | + * | |
| 511 | 533 | * Returns an array matching the receipt data schema with all sections |
| 512 | - * 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. | |
| 513 | 536 | * |
| 514 | 537 | * @param object|null $pos_store POS store object. Falls back to default store. |
| 515 | 538 | * |
| 516 | 539 | * @return array Complete receipt data array. |
| 517 | 540 | */ |
| 518 | - public function build( $pos_store = null ): array { | |
| 541 | + public function sample( $pos_store = null ): array { | |
| 519 | 542 | $resolved_store = null === $pos_store ? wcpos_get_store() : $pos_store; |
| 520 | 543 | if ( ! \is_object( $resolved_store ) ) { |
| 521 | 544 | $resolved_store = wcpos_get_store(); |
| 522 | 545 | } |
| 523 | - $this->pos_store = \is_object( $resolved_store ) ? $resolved_store : new Store(); | |
| 524 | - $this->store_resolver = new Receipt_Store_Resolver( $this->pos_store ); | |
| 546 | + $this->store_resolver = new Receipt_Store_Resolver( \is_object( $resolved_store ) ? $resolved_store : new Store() ); | |
| 525 | 547 | $currency = $this->resolve_currency(); |
| 526 | 548 | $display_incl = 'incl' === $this->store_resolver->resolve_store_option_string( |
| 527 | 549 | 'get_tax_display_cart', |
| 528 | 550 | get_option( 'woocommerce_tax_display_cart', 'excl' ) |
| @@ -543,9 +565,9 @@ | ||
| 543 | 565 | ); |
| 544 | 566 | $dp = $this->store_resolver->resolve_price_num_decimals(); |
| 545 | 567 | |
| 546 | 568 | // Build line items. |
| 547 | - $lines = array(); | |
| 569 | + $priced_lines = array(); | |
| 548 | 570 | $lines_total_excl = 0.0; |
| 549 | 571 | $lines_total_incl = 0.0; |
| 550 | 572 | |
| 551 | 573 | foreach ( $raw_products as $index => $product ) { |
| @@ -577,54 +599,51 @@ | ||
| 577 | 599 | $line_regular_excl = round( $regular_unit_excl * $qty, $dp ); |
| 578 | 600 | $line_savings_incl = round( $unit_savings_incl * $qty, $dp ); |
| 579 | 601 | $line_savings_excl = round( $unit_savings_excl * $qty, $dp ); |
| 580 | 602 | |
| 581 | - $unit_price_rounded = $display_incl ? $selling_unit_incl : $selling_unit_excl; | |
| 582 | - | |
| 583 | - $lines[] = array( | |
| 584 | - 'key' => (string) ( $index + 1 ), | |
| 585 | - 'sku' => $product['sku'], | |
| 586 | - 'name' => $product['name'], | |
| 587 | - 'qty' => (float) $qty, | |
| 588 | - 'qty_refunded' => 0.0, | |
| 589 | - 'regular_price' => $display_incl ? $regular_unit_incl : $regular_unit_excl, | |
| 590 | - 'regular_price_incl' => $regular_unit_incl, | |
| 591 | - 'regular_price_excl' => $regular_unit_excl, | |
| 592 | - 'selling_price' => $unit_price_rounded, | |
| 593 | - 'selling_price_incl' => $selling_unit_incl, | |
| 594 | - 'selling_price_excl' => $selling_unit_excl, | |
| 595 | - 'unit_savings' => $display_incl ? $unit_savings_incl : $unit_savings_excl, | |
| 596 | - 'unit_savings_incl' => $unit_savings_incl, | |
| 597 | - 'unit_savings_excl' => $unit_savings_excl, | |
| 598 | - 'line_regular_total' => $display_incl ? $line_regular_incl : $line_regular_excl, | |
| 599 | - 'line_regular_total_incl' => $line_regular_incl, | |
| 600 | - 'line_regular_total_excl' => $line_regular_excl, | |
| 601 | - 'line_selling_total' => $display_incl ? $line_total_incl : $line_total_excl, | |
| 602 | - 'line_selling_total_incl' => $line_total_incl, | |
| 603 | - 'line_selling_total_excl' => $line_total_excl, | |
| 604 | - 'line_savings' => $display_incl ? $line_savings_incl : $line_savings_excl, | |
| 605 | - 'line_savings_incl' => $line_savings_incl, | |
| 606 | - '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 | + ), | |
| 607 | 641 | 'savings_in_discounts' => false, |
| 608 | - 'unit_subtotal' => $unit_price_rounded, | |
| 609 | - 'unit_subtotal_incl' => $selling_unit_incl, | |
| 610 | - 'unit_subtotal_excl' => $selling_unit_excl, | |
| 611 | - 'unit_price' => $unit_price_rounded, | |
| 612 | - 'unit_price_incl' => $selling_unit_incl, | |
| 613 | - 'unit_price_excl' => $selling_unit_excl, | |
| 614 | - 'line_subtotal' => $display_incl ? $line_total_incl : $line_total_excl, | |
| 615 | - 'line_subtotal_incl' => $line_total_incl, | |
| 616 | - 'line_subtotal_excl' => $line_total_excl, | |
| 617 | - 'discounts' => 0.0, | |
| 618 | - 'discounts_incl' => 0.0, | |
| 619 | - 'discounts_excl' => 0.0, | |
| 620 | - 'line_total' => $display_incl ? $line_total_incl : $line_total_excl, | |
| 621 | - 'line_total_incl' => $line_total_incl, | |
| 622 | - 'line_total_excl' => $line_total_excl, | |
| 623 | - 'total_refunded' => 0.0, | |
| 624 | - 'taxes' => array(), | |
| 625 | - 'meta' => $product['meta'] ?? array(), | |
| 626 | - 'attributes' => $product['attributes'] ?? array(), | |
| 642 | + 'total_refunded' => 0.0, | |
| 643 | + 'taxes' => array(), | |
| 644 | + 'meta' => $product['meta'] ?? array(), | |
| 645 | + 'attributes' => $product['attributes'] ?? array(), | |
| 627 | 646 | ); |
| 628 | 647 | |
| 629 | 648 | $lines_total_excl += $line_total_excl; |
| 630 | 649 | $lines_total_incl += $line_total_incl; |
| @@ -659,52 +678,47 @@ | ||
| 659 | 678 | |
| 660 | 679 | // Distribute discount proportionally across line items with remainder correction. |
| 661 | 680 | $sum_discount_excl = 0.0; |
| 662 | 681 | $sum_discount_incl = 0.0; |
| 663 | - $last_index = count( $lines ) - 1; | |
| 682 | + $last_index = count( $priced_lines ) - 1; | |
| 664 | 683 | |
| 665 | - foreach ( $lines as $i => &$line ) { | |
| 684 | + foreach ( $priced_lines as $i => &$line ) { | |
| 666 | 685 | if ( $lines_total_excl > 0 ) { |
| 667 | - $share = $line['line_subtotal_excl'] / $lines_total_excl; | |
| 686 | + $share = $line['line_subtotal']['excl'] / $lines_total_excl; | |
| 668 | 687 | } else { |
| 669 | - $share = 1.0 / count( $lines ); | |
| 688 | + $share = 1.0 / count( $priced_lines ); | |
| 670 | 689 | } |
| 671 | 690 | |
| 672 | 691 | if ( $i < $last_index ) { |
| 673 | - $line['discounts_excl'] = round( $discount_excl * $share, $dp ); | |
| 674 | - $line['discounts_incl'] = round( $discount_incl * $share, $dp ); | |
| 675 | - $sum_discount_excl += $line['discounts_excl']; | |
| 676 | - $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']; | |
| 677 | 696 | } else { |
| 678 | 697 | // Assign remainder to last item so distributed totals match exactly. |
| 679 | - $line['discounts_excl'] = round( $discount_excl - $sum_discount_excl, $dp ); | |
| 680 | - $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 ); | |
| 681 | 700 | } |
| 682 | 701 | |
| 683 | - $line['discounts'] = $display_incl ? $line['discounts_incl'] : $line['discounts_excl']; | |
| 684 | - $line['line_total_excl'] = round( $line['line_subtotal_excl'] - $line['discounts_excl'], $dp ); | |
| 685 | - $line['line_total_incl'] = round( $line['line_subtotal_incl'] - $line['discounts_incl'], $dp ); | |
| 686 | - $line['line_total'] = $display_incl ? $line['line_total_incl'] : $line['line_total_excl']; | |
| 687 | - $line['unit_price_incl'] = round( $line['line_total_incl'] / $line['qty'], $dp ); | |
| 688 | - $line['unit_price_excl'] = round( $line['line_total_excl'] / $line['qty'], $dp ); | |
| 689 | - $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 ); | |
| 690 | 706 | } |
| 691 | 707 | unset( $line ); |
| 692 | 708 | |
| 693 | - // Totals. | |
| 694 | - $subtotal_excl = $lines_total_excl; | |
| 695 | - $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 | + ); | |
| 696 | 715 | |
| 697 | - // Item count summaries — matches the real builder's totals.total_qty | |
| 698 | - // / totals.line_count so preview mirrors the live receipt schema. | |
| 699 | - $total_qty = (float) array_sum( array_column( $lines, 'qty' ) ); | |
| 700 | - $line_count = \count( $lines ); | |
| 701 | - | |
| 702 | 716 | // Taxable base: line items - discount + shipping + fee (all excl). |
| 703 | - $taxable_excl = $subtotal_excl - $discount_excl + $shipping_excl + $fee_excl; | |
| 717 | + $taxable_excl = $lines_total_excl - $discount_excl + $shipping_excl + $fee_excl; | |
| 704 | 718 | $total_tax = round( $taxable_excl * $tax_rate / 100, $dp ); |
| 705 | 719 | |
| 706 | - $total_excl = $subtotal_excl - $discount_excl + $shipping_excl + $fee_excl; | |
| 720 | + $total_excl = $lines_total_excl - $discount_excl + $shipping_excl + $fee_excl; | |
| 707 | 721 | $total_incl = $total_excl + $total_tax; |
| 708 | 722 | |
| 709 | 723 | // Payment: cash rounded up to nearest 5. |
| 710 | 724 | $tendered = (float) ( ceil( $total_incl / 5 ) * 5 ); |
| @@ -721,8 +735,9 @@ | ||
| 721 | 735 | 'number' => '1234', |
| 722 | 736 | 'currency' => $currency, |
| 723 | 737 | 'customer_note' => __( 'Please gift wrap this order. Thank you!', 'woocommerce-pos' ), |
| 724 | 738 | 'wc_status' => 'completed', |
| 739 | + 'status_label' => wc_get_order_status_name( 'completed' ), | |
| 725 | 740 | 'created_via' => 'woocommerce-pos', |
| 726 | 741 | 'created' => Receipt_Date_Formatter::from_timestamp( $created_timestamp, $date_timezone, $date_locale ), |
| 727 | 742 | 'paid' => Receipt_Date_Formatter::from_timestamp( $paid_timestamp, $date_timezone, $date_locale ), |
| 728 | 743 | 'completed' => Receipt_Date_Formatter::from_timestamp( $completed_timestamp, $date_timezone, $date_locale ), |
| @@ -737,95 +752,72 @@ | ||
| 737 | 752 | |
| 738 | 753 | $customer = $this->get_customer(); |
| 739 | 754 | |
| 740 | 755 | $fees = array( |
| 741 | - array( | |
| 742 | - 'label' => $fee_label, | |
| 743 | - 'total' => $display_incl ? $fee_incl : $fee_excl, | |
| 744 | - 'total_incl' => $fee_incl, | |
| 745 | - 'total_excl' => $fee_excl, | |
| 746 | - 'taxes' => array(), | |
| 747 | - '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 | |
| 748 | 765 | ), |
| 749 | 766 | ); |
| 750 | - | |
| 751 | 767 | $shipping = array( |
| 752 | - array( | |
| 753 | - 'label' => $shipping_label, | |
| 754 | - 'method_id' => 'flat_rate', | |
| 755 | - 'total' => $display_incl ? $shipping_incl : $shipping_excl, | |
| 756 | - 'total_incl' => $shipping_incl, | |
| 757 | - 'total_excl' => $shipping_excl, | |
| 758 | - 'taxes' => array(), | |
| 759 | - '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 | |
| 760 | 778 | ), |
| 761 | 779 | ); |
| 762 | - | |
| 763 | 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, | |
| 764 | 794 | array( |
| 765 | - 'label' => $discount_label, | |
| 766 | - 'code' => 'SUMMER10', | |
| 767 | - 'total' => $display_incl ? $discount_incl : $discount_excl, | |
| 768 | - 'total_incl' => $discount_incl, | |
| 769 | - '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, | |
| 770 | 804 | ), |
| 805 | + $display_incl | |
| 771 | 806 | ); |
| 772 | 807 | |
| 773 | - $sale_savings_total_incl = (float) array_sum( array_column( $lines, 'line_savings_incl' ) ); | |
| 774 | - $sale_savings_total_excl = (float) array_sum( array_column( $lines, 'line_savings_excl' ) ); | |
| 775 | - $total_saved_incl = $discount_incl + $sale_savings_total_incl; | |
| 776 | - $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(); | |
| 777 | 811 | |
| 778 | - $totals = array( | |
| 779 | - 'subtotal' => $display_incl ? $subtotal_incl : $subtotal_excl, | |
| 780 | - 'subtotal_incl' => $subtotal_incl, | |
| 781 | - 'subtotal_excl' => $subtotal_excl, | |
| 782 | - 'discount_total' => $display_incl ? $discount_incl : $discount_excl, | |
| 783 | - 'discount_total_incl' => $discount_incl, | |
| 784 | - 'discount_total_excl' => $discount_excl, | |
| 785 | - 'sale_savings_total' => $display_incl ? $sale_savings_total_incl : $sale_savings_total_excl, | |
| 786 | - 'sale_savings_total_incl' => $sale_savings_total_incl, | |
| 787 | - 'sale_savings_total_excl' => $sale_savings_total_excl, | |
| 788 | - 'total_saved' => $display_incl ? $total_saved_incl : $total_saved_excl, | |
| 789 | - 'total_saved_incl' => $total_saved_incl, | |
| 790 | - 'total_saved_excl' => $total_saved_excl, | |
| 791 | - 'total_saved_complete' => true, | |
| 792 | - 'tax_total' => $total_tax, | |
| 793 | - 'total' => $display_incl ? $total_incl : $total_excl, | |
| 794 | - 'total_incl' => $total_incl, | |
| 795 | - 'total_excl' => $total_excl, | |
| 796 | - 'paid_total' => $total_incl, | |
| 797 | - 'change_total' => $change_total, | |
| 798 | - 'total_qty' => $total_qty, | |
| 799 | - 'line_count' => $line_count, | |
| 800 | - ); | |
| 801 | - | |
| 802 | - $taxable_amount_incl = $taxable_excl + $total_tax; | |
| 803 | - | |
| 804 | - if ( $tax_rate > 0 ) { | |
| 805 | - $tax_summary = array( | |
| 806 | - array( | |
| 807 | - 'code' => $tax_code, | |
| 808 | - 'rate' => $tax_rate, | |
| 809 | - 'label' => $tax_label, | |
| 810 | - 'compound' => false, | |
| 811 | - 'taxable_amount_excl' => $taxable_excl, | |
| 812 | - 'tax_amount' => $total_tax, | |
| 813 | - 'taxable_amount_incl' => $taxable_amount_incl, | |
| 814 | - ), | |
| 815 | - ); | |
| 816 | - } else { | |
| 817 | - $tax_summary = array(); | |
| 818 | - } | |
| 819 | - | |
| 820 | 812 | $payments = array( |
| 821 | - array( | |
| 822 | - 'method_id' => 'pos_cash', | |
| 823 | - 'method_title' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Cash', 'woocommerce-pos' ), | |
| 824 | - 'amount' => $total_incl, | |
| 825 | - 'transaction_id' => '', | |
| 826 | - 'tendered' => $tendered, | |
| 827 | - '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 | |
| 828 | 820 | ), |
| 829 | 821 | ); |
| 830 | 822 | |
| 831 | 823 | $refunds = array(); |
| @@ -832,50 +824,52 @@ | ||
| 832 | 824 | |
| 833 | 825 | $presentation_hints = $this->store_resolver->build_presentation_hints( $currency, $prices_include_tax ); |
| 834 | 826 | $tax = $this->store_resolver->build_tax_section(); |
| 835 | 827 | |
| 836 | - $fiscal = array( | |
| 837 | - 'immutable_id' => '12345:42', | |
| 838 | - 'receipt_number' => '00042', | |
| 839 | - 'sequence' => 42, | |
| 840 | - 'hash' => 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2', | |
| 841 | - 'qr_payload' => 'https://example.com/verify?id=SAMPLE-001', | |
| 842 | - 'tax_agency_code' => 'SAMPLE', | |
| 843 | - 'signed_at' => gmdate( 'Y-m-d\TH:i:s\Z' ), | |
| 844 | - 'signature_excerpt' => 'A1B2', | |
| 845 | - 'document_label' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Tax Receipt', 'woocommerce-pos' ), | |
| 846 | - 'is_reprint' => false, | |
| 847 | - 'reprint_count' => 0, | |
| 848 | - 'extra_fields' => array( | |
| 849 | - array( | |
| 850 | - 'label' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Tax ID', 'woocommerce-pos' ), | |
| 851 | - 'value' => 'XX-1234567', | |
| 828 | + $fiscal = Receipt_Payload_Assembler::fiscal( | |
| 829 | + array( | |
| 830 | + 'immutable_id' => '12345:42', | |
| 831 | + 'receipt_number' => '00042', | |
| 832 | + 'sequence' => 42, | |
| 833 | + 'hash' => 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2', | |
| 834 | + 'qr_payload' => 'https://example.com/verify?id=SAMPLE-001', | |
| 835 | + 'tax_agency_code' => 'SAMPLE', | |
| 836 | + 'signed_at' => gmdate( 'Y-m-d\TH:i:s\Z' ), | |
| 837 | + 'signature_excerpt' => 'A1B2', | |
| 838 | + 'document_label' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Tax Receipt', 'woocommerce-pos' ), | |
| 839 | + 'is_reprint' => false, | |
| 840 | + 'reprint_count' => 0, | |
| 841 | + 'extra_fields' => array( | |
| 842 | + array( | |
| 843 | + 'label' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Tax ID', 'woocommerce-pos' ), | |
| 844 | + 'value' => 'XX-1234567', | |
| 845 | + ), | |
| 846 | + array( | |
| 847 | + 'label' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Auth Code', 'woocommerce-pos' ), | |
| 848 | + 'value' => 'ABC-789', | |
| 849 | + ), | |
| 852 | 850 | ), |
| 853 | - array( | |
| 854 | - 'label' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Auth Code', 'woocommerce-pos' ), | |
| 855 | - 'value' => 'ABC-789', | |
| 856 | - ), | |
| 857 | - ), | |
| 851 | + ) | |
| 858 | 852 | ); |
| 859 | 853 | |
| 860 | - return array( | |
| 861 | - 'order' => $order, | |
| 862 | - 'store' => $store, | |
| 863 | - 'cashier' => $cashier, | |
| 864 | - 'customer' => $customer, | |
| 865 | - 'lines' => $lines, | |
| 866 | - 'fees' => $fees, | |
| 867 | - 'shipping' => $shipping, | |
| 868 | - 'discounts' => $discounts, | |
| 869 | - 'totals' => $totals, | |
| 870 | - 'tax' => $tax, | |
| 871 | - 'tax_summary' => $tax_summary, | |
| 872 | - 'has_tax_summary' => ! empty( $tax_summary ), | |
| 873 | - 'payments' => $payments, | |
| 874 | - 'refunds' => $refunds, | |
| 875 | - 'fiscal' => $fiscal, | |
| 876 | - 'presentation_hints' => $presentation_hints, | |
| 877 | - 'i18n' => Receipt_I18n_Labels::get_labels( $presentation_hints['locale'] ?? '' ), | |
| 854 | + return Receipt_Payload_Assembler::assemble( | |
| 855 | + array( | |
| 856 | + 'order' => $order, | |
| 857 | + 'store' => $store, | |
| 858 | + 'cashier' => $cashier, | |
| 859 | + 'customer' => $customer, | |
| 860 | + 'lines' => $lines, | |
| 861 | + 'fees' => $fees, | |
| 862 | + 'shipping' => $shipping, | |
| 863 | + 'discounts' => $discounts, | |
| 864 | + 'totals' => $totals, | |
| 865 | + 'tax' => $tax, | |
| 866 | + 'tax_summary' => $tax_summary, | |
| 867 | + 'payments' => $payments, | |
| 868 | + 'refunds' => $refunds, | |
| 869 | + 'fiscal' => $fiscal, | |
| 870 | + 'presentation_hints' => $presentation_hints, | |
| 871 | + ) | |
| 878 | 872 | ); |
| 879 | 873 | } |
| 880 | 874 | |
| 881 | 875 | /** |
| @@ -883,84 +877,26 @@ | ||
| 883 | 877 | * |
| 884 | 878 | * @return array Store data with name, address, branding, and policy fields. |
| 885 | 879 | */ |
| 886 | 880 | private function get_store_info(): array { |
| 887 | - $pos_store = $this->pos_store; | |
| 888 | - $store_id = (int) $this->store_resolver->get_store_value( 'get_id', 0 ); | |
| 889 | - $store_name = (string) $this->store_resolver->get_store_value( 'get_name', '' ); | |
| 890 | - $store_address = (string) $this->store_resolver->get_store_value( 'get_store_address', '' ); | |
| 891 | - $store_address_2 = (string) $this->store_resolver->get_store_value( 'get_store_address_2', '' ); | |
| 892 | - $store_city = (string) $this->store_resolver->get_store_value( 'get_store_city', '' ); | |
| 893 | - $store_postcode = (string) $this->store_resolver->get_store_value( 'get_store_postcode', '' ); | |
| 894 | - $store_country = (string) $this->store_resolver->get_store_value( 'get_store_country', '' ); | |
| 895 | - $store_state = (string) $this->store_resolver->get_store_value( 'get_store_state', '' ); | |
| 896 | - $store_phone = (string) $this->store_resolver->get_store_value( 'get_phone', '' ); | |
| 897 | - $store_email = (string) $this->store_resolver->get_store_value( 'get_email', '' ); | |
| 898 | - $store_tax_ids = $this->store_resolver->get_store_value( 'get_tax_ids', array() ); | |
| 899 | - $store_tax_ids = is_array( $store_tax_ids ) ? $store_tax_ids : array(); | |
| 900 | - $store_tax_ids = Receipt_Store_Resolver::with_store_tax_id_labels( $store_tax_ids, $this->store_resolver->resolve_locale() ); | |
| 901 | - | |
| 902 | - $store_address_parts = array( | |
| 903 | - 'address_1' => $store_address, | |
| 904 | - 'address_2' => $store_address_2, | |
| 905 | - 'city' => $store_city, | |
| 906 | - 'state' => $store_state, | |
| 907 | - 'postcode' => $store_postcode, | |
| 908 | - 'country' => $store_country, | |
| 881 | + return $this->store_resolver->build_store_section( | |
| 882 | + array( | |
| 883 | + // Sample content stands in wherever the store has nothing configured, | |
| 884 | + // so preview templates always have every section to lay out. | |
| 885 | + 'opening_hours' => array( | |
| 886 | + '0' => array( '09:00', '17:00' ), | |
| 887 | + '1' => array( '09:00', '17:00' ), | |
| 888 | + '2' => array( '09:00', '17:00' ), | |
| 889 | + '3' => array( '09:00', '17:00' ), | |
| 890 | + '4' => array( '09:00', '17:00' ), | |
| 891 | + '5' => array( '10:00', '16:00' ), | |
| 892 | + '6' => array(), | |
| 893 | + ), | |
| 894 | + 'personal_notes' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Thank you for shopping with us! We appreciate your business.', 'woocommerce-pos' ), | |
| 895 | + 'policies_and_conditions' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Returns accepted within 30 days with original receipt. Items must be unused and in original packaging.', 'woocommerce-pos' ), | |
| 896 | + 'footer_imprint' => /* translators: Sample receipt label or value used in receipt template previews. */ __( 'Thank you for your purchase!', 'woocommerce-pos' ), | |
| 897 | + ) | |
| 909 | 898 | ); |
| 910 | - | |
| 911 | - $store = array( | |
| 912 | - 'id' => $store_id, | |
| 913 | - 'name' => '' !== $store_name ? $store_name : get_bloginfo( 'name' ), | |
| 914 | - 'address' => $store_address_parts, | |
| 915 | - 'address_lines' => Receipt_Store_Resolver::compose_address_lines( $store_address_parts ), | |
| 916 | - 'tax_ids' => $store_tax_ids, | |
| 917 | - 'phone' => $store_phone, | |
| 918 | - 'email' => $store_email, | |
| 919 | - ); | |
| 920 | - | |
| 921 | - $opening_hours_raw = $this->store_resolver->get_store_value( 'get_opening_hours', array() ); | |
| 922 | - $personal_notes = (string) $this->store_resolver->get_store_value( 'get_personal_notes', '' ); | |
| 923 | - $policies_and_conditions = (string) $this->store_resolver->get_store_value( 'get_policies_and_conditions', '' ); | |
| 924 | - $footer_imprint = (string) $this->store_resolver->get_store_value( 'get_footer_imprint', '' ); | |
| 925 | - | |
| 926 | - if ( ! empty( $opening_hours_raw ) && \is_array( $opening_hours_raw ) ) { | |
| 927 | - $store['opening_hours'] = Opening_Hours_Formatter::format_compact( $opening_hours_raw ); | |
| 928 | - $store['opening_hours_vertical'] = Opening_Hours_Formatter::format_vertical( $opening_hours_raw ); | |
| 929 | - $store['opening_hours_inline'] = Opening_Hours_Formatter::format_inline( $opening_hours_raw ); | |
| 930 | - } elseif ( \is_string( $opening_hours_raw ) && '' !== trim( $opening_hours_raw ) ) { | |
| 931 | - $store['opening_hours'] = $opening_hours_raw; | |
| 932 | - $store['opening_hours_vertical'] = null; | |
| 933 | - $store['opening_hours_inline'] = null; | |
| 934 | - } else { | |
| 935 | - // Sample hours for preview when none configured. | |
| 936 | - $opening_hours_raw = array( | |
| 937 | - '0' => array( '09:00', '17:00' ), | |
| 938 | - '1' => array( '09:00', '17:00' ), | |
| 939 | - '2' => array( '09:00', '17:00' ), | |
| 940 | - '3' => array( '09:00', '17:00' ), | |
| 941 | - '4' => array( '09:00', '17:00' ), | |
| 942 | - '5' => array( '10:00', '16:00' ), | |
| 943 | - '6' => array(), | |
| 944 | - ); | |
| 945 | - $store['opening_hours'] = Opening_Hours_Formatter::format_compact( $opening_hours_raw ); | |
| 946 | - $store['opening_hours_vertical'] = Opening_Hours_Formatter::format_vertical( $opening_hours_raw ); | |
| 947 | - $store['opening_hours_inline'] = Opening_Hours_Formatter::format_inline( $opening_hours_raw ); | |
| 948 | - } | |
| 949 | - $store['logo'] = Store_Logo_Resolver::resolve( $pos_store ); | |
| 950 | - $opening_hours_notes = (string) $this->store_resolver->get_store_value( 'get_opening_hours_notes', '' ); | |
| 951 | - $store['opening_hours_notes'] = '' !== $opening_hours_notes ? $opening_hours_notes : null; | |
| 952 | - $store['personal_notes'] = ( null !== $personal_notes && '' !== $personal_notes ) | |
| 953 | - ? $personal_notes | |
| 954 | - : __( 'Thank you for shopping with us! We appreciate your business.', 'woocommerce-pos' ); | |
| 955 | - $store['policies_and_conditions'] = ( null !== $policies_and_conditions && '' !== $policies_and_conditions ) | |
| 956 | - ? $policies_and_conditions | |
| 957 | - : __( 'Returns accepted within 30 days with original receipt. Items must be unused and in original packaging.', 'woocommerce-pos' ); | |
| 958 | - $store['footer_imprint'] = ( null !== $footer_imprint && '' !== $footer_imprint ) | |
| 959 | - ? $footer_imprint | |
| 960 | - : __( 'Thank you for your purchase!', 'woocommerce-pos' ); | |
| 961 | - | |
| 962 | - return $store; | |
| 963 | 899 | } |
| 964 | 900 | |
| 965 | 901 | |
| 966 | 902 | /** |
| @@ -970,12 +906,12 @@ | ||
| 970 | 906 | * |
| 971 | 907 | * @return string ISO 4217 currency code (e.g. "USD", "EUR"). |
| 972 | 908 | */ |
| 973 | 909 | private function resolve_currency(): string { |
| 974 | - $fallback = get_option( 'woocommerce_currency', 'USD' ); | |
| 975 | - $store_currency = (string) $this->store_resolver->get_store_value( 'get_currency', '' ); | |
| 976 | - | |
| 977 | - return '' !== $store_currency ? $store_currency : $fallback; | |
| 910 | + return $this->store_resolver->resolve_store_option_string( | |
| 911 | + 'get_currency', | |
| 912 | + get_option( 'woocommerce_currency', 'USD' ) | |
| 913 | + ); | |
| 978 | 914 | } |
| 979 | 915 | |
| 980 | 916 | |
| 981 | 917 | /** |
| @@ -1151,19 +1087,10 @@ | ||
| 1151 | 1087 | ), |
| 1152 | 1088 | ); |
| 1153 | 1089 | |
| 1154 | 1090 | $tax_ids = $samples[ $country ] ?? array(); |
| 1155 | - $labels = Receipt_I18n_Labels::get_labels( $locale ); | |
| 1156 | 1091 | |
| 1157 | - return array_map( | |
| 1158 | - static function ( array $tax_id ) use ( $labels ): array { | |
| 1159 | - $type = (string) $tax_id['type']; | |
| 1160 | - $tax_id['label'] = $labels[ 'customer_tax_id_label_' . $type ] ?? $labels['customer_tax_id_label_other']; | |
| 1161 | - | |
| 1162 | - return $tax_id; | |
| 1163 | - }, | |
| 1164 | - $tax_ids | |
| 1165 | - ); | |
| 1092 | + return Receipt_Sections::label_tax_ids( $tax_ids, 'customer', $locale ); | |
| 1166 | 1093 | } |
| 1167 | 1094 | |
| 1168 | 1095 | /** |
| 1169 | 1096 | * Get product data for line items. |
| @@ -1196,19 +1123,9 @@ | ||
| 1196 | 1123 | if ( ! empty( $children ) ) { |
| 1197 | 1124 | $variation = wc_get_product( $children[0] ); |
| 1198 | 1125 | if ( $variation instanceof \WC_Product_Variation ) { |
| 1199 | 1126 | $product = $variation; |
| 1200 | - foreach ( $variation->get_variation_attributes() as $attr_key => $attr_value ) { | |
| 1201 | - if ( '' !== $attr_value ) { | |
| 1202 | - $taxonomy = str_replace( 'attribute_', '', $attr_key ); | |
| 1203 | - $label = wc_attribute_label( $taxonomy, $variation ); | |
| 1204 | - $value = $variation->get_attribute( $taxonomy ); | |
| 1205 | - $meta[] = array( | |
| 1206 | - 'key' => wp_strip_all_tags( $label ), | |
| 1207 | - 'value' => wp_strip_all_tags( '' !== $value ? $value : $attr_value ), | |
| 1208 | - ); | |
| 1209 | - } | |
| 1210 | - } | |
| 1127 | + $meta = Receipt_Sections::variation_attribute_pairs( $variation ); | |
| 1211 | 1128 | } |
| 1212 | 1129 | } |
| 1213 | 1130 | } |
| 1214 | 1131 | |