PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.18
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.18
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 1.9.14 All 163 releases
woocommerce-pos / includes / Services / Receipt_Sections.php

Receipt_Sections.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.18, at includes/Services/Receipt_Sections.php

324 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Shared receipt contract row shapes and aggregate rules.
4 *
5 * @package WCPOS\WooCommercePOS\Services
6 */
7
8 namespace WCPOS\WooCommercePOS\Services;
9
10 /** Receipt sections accept priced values; builders own order reading and pricing. */
11 final class Receipt_Sections {
12 /**
13 * Shape one line in live receipt key order, preserving nullable price bases.
14 *
15 * @param array $line Identity, metadata and incl/excl amount pairs.
16 * @param bool $display_incl Whether bare amounts include tax.
17 * @return array
18 */
19 public static function line( array $line, bool $display_incl ): array {
20 $row = array();
21 foreach ( array( 'key', 'sku', 'name', 'qty', 'qty_refunded' ) as $key ) {
22 $row[ $key ] = $line[ $key ];
23 }
24 foreach ( array( 'unit_subtotal', 'unit_price', 'line_subtotal', 'discounts', 'line_total' ) as $key ) {
25 $row = array_merge( $row, self::triple( $key, $line[ $key ], $display_incl ) );
26 }
27 foreach ( array( 'total_refunded', 'taxes', 'meta', 'attributes' ) as $key ) {
28 $row[ $key ] = $line[ $key ];
29 }
30 foreach ( array( 'regular_price', 'selling_price', 'unit_savings', 'line_regular_total', 'line_selling_total', 'line_savings' ) as $key ) {
31 $row = array_merge( $row, self::triple( $key, $line[ $key ], $display_incl ) );
32 }
33 $row['savings_in_discounts'] = $line['savings_in_discounts'];
34 return $row;
35 }
36
37 /**
38 * Shape a fee row.
39 *
40 * @param string $label Display label.
41 * @param array $total Incl/excl amounts.
42 * @param array $taxes Item taxes.
43 * @param array $meta Metadata pairs.
44 * @param bool $display_incl Whether bare amounts include tax.
45 * @return array
46 */
47 public static function fee( string $label, array $total, array $taxes, array $meta, bool $display_incl ): array {
48 return array_merge(
49 array( 'label' => $label ),
50 self::triple( 'total', $total, $display_incl ),
51 array(
52 'taxes' => $taxes,
53 'meta' => $meta,
54 )
55 );
56 }
57
58 /**
59 * Shape a shipping row.
60 *
61 * @param string $label Display label.
62 * @param string $method_id Shipping method.
63 * @param array $total Incl/excl amounts.
64 * @param array $taxes Item taxes.
65 * @param array $meta Metadata pairs.
66 * @param bool $display_incl Whether bare amounts include tax.
67 * @return array
68 */
69 public static function shipping( string $label, string $method_id, array $total, array $taxes, array $meta, bool $display_incl ): array {
70 return array_merge(
71 array(
72 'label' => $label,
73 'method_id' => $method_id,
74 ),
75 self::triple( 'total', $total, $display_incl ),
76 array(
77 'taxes' => $taxes,
78 'meta' => $meta,
79 )
80 );
81 }
82
83 /**
84 * Shape a discount row.
85 *
86 * @param string $label Display label.
87 * @param string $code Coupon code.
88 * @param string $discount_type Coupon type.
89 * @param array $total Incl/excl amounts.
90 * @param bool $display_incl Whether bare amounts include tax.
91 * @return array
92 */
93 public static function discount( string $label, string $code, string $discount_type, array $total, bool $display_incl ): array {
94 return array_merge(
95 array(
96 'label' => $label,
97 'code' => $code,
98 'discount_type' => $discount_type,
99 ),
100 self::triple( 'total', $total, $display_incl )
101 );
102 }
103
104 /**
105 * Shape a payment row.
106 *
107 * @param string $method_id Payment method.
108 * @param string $method_title Display title.
109 * @param float $amount Paid amount.
110 * @param string $transaction_id Transaction reference.
111 * @param float $tendered Tendered amount.
112 * @param float $change Change returned.
113 * @return array
114 */
115 public static function payment( string $method_id, string $method_title, float $amount, string $transaction_id, float $tendered, float $change ): array {
116 return array(
117 'method_id' => $method_id,
118 'method_title' => $method_title,
119 'amount' => $amount,
120 'transaction_id' => $transaction_id,
121 'tendered' => $tendered,
122 'change' => $change,
123 );
124 }
125
126 /**
127 * Shape a tax summary, keeping unknown rates and bases null.
128 *
129 * @param string $code Rate identifier.
130 * @param float $rate Percentage rate.
131 * @param string $label Display label.
132 * @param bool $compound Whether the rate is compound.
133 * @param float|null $taxable_excl Known taxable base, if any.
134 * @param float $tax_amount Tax charged.
135 * @return array
136 */
137 public static function tax_summary_entry( string $code, float $rate, string $label, bool $compound, ?float $taxable_excl, float $tax_amount ): array {
138 return array(
139 'code' => $code,
140 'rate' => $rate > 0 ? $rate : null,
141 'label' => $label,
142 'compound' => $compound,
143 'taxable_amount_excl' => $taxable_excl,
144 'tax_amount' => $tax_amount,
145 'taxable_amount_incl' => null !== $taxable_excl ? $taxable_excl + $tax_amount : null,
146 );
147 }
148
149 /**
150 * Aggregate shaped lines and order-level amounts into receipt totals.
151 *
152 * @param array $lines Shaped line rows.
153 * @param array $amounts Discount pair, tax, inclusive total, paid, change and refund totals.
154 * @param bool $display_incl Whether bare amounts include tax.
155 * @return array
156 */
157 public static function totals( array $lines, array $amounts, bool $display_incl ): array {
158 // Legacy POS lines already include regular-to-selling savings in WooCommerce's
159 // discount total. Add only current-shape savings to total_saved to avoid overlap.
160 $sale_savings_totals = array(
161 'incl' => 0.0,
162 'excl' => 0.0,
163 );
164 $additional_savings = array(
165 'incl' => 0.0,
166 'excl' => 0.0,
167 );
168 $savings_complete = array(
169 'incl' => true,
170 'excl' => true,
171 );
172 $price_precision = wc_get_price_decimals();
173 foreach ( $lines as $line ) {
174 foreach ( array( 'incl', 'excl' ) as $basis ) {
175 $key = 'line_savings_' . $basis;
176 if ( ! isset( $line[ $key ] ) || ! is_numeric( $line[ $key ] ) ) {
177 $savings_complete[ $basis ] = false;
178 continue;
179 }
180
181 $line_savings = (float) $line[ $key ];
182 $sale_savings_totals[ $basis ] += $line_savings;
183 if ( empty( $line['savings_in_discounts'] ) ) {
184 $subtotal_key = 'line_subtotal_' . $basis;
185 $selling_key = 'line_selling_total_' . $basis;
186 if (
187 $line_savings > 0.0
188 && (
189 ! isset( $line[ $subtotal_key ], $line[ $selling_key ] )
190 || round( abs( (float) $line[ $subtotal_key ] - (float) $line[ $selling_key ] ), $price_precision ) > 0.0
191 )
192 ) {
193 $savings_complete[ $basis ] = false;
194 continue;
195 }
196 $additional_savings[ $basis ] += $line_savings;
197 }
198 }
199 }
200
201 $total_saved = array(
202 'incl' => $savings_complete['incl'] ? $amounts['discount_total']['incl'] + $additional_savings['incl'] : null,
203 'excl' => $savings_complete['excl'] ? $amounts['discount_total']['excl'] + $additional_savings['excl'] : null,
204 );
205 foreach ( array( 'incl', 'excl' ) as $basis ) {
206 if ( ! $savings_complete[ $basis ] ) {
207 $sale_savings_totals[ $basis ] = null;
208 }
209 }
210 $display_basis = $display_incl ? 'incl' : 'excl';
211
212 $subtotal_excl = array_sum( array_column( $lines, 'line_subtotal_excl' ) );
213 $subtotal_incl = array_sum( array_column( $lines, 'line_subtotal_incl' ) );
214
215 // Item count summaries — useful for packing slips and kitchen tickets
216 // where Mustache can't sum/count an array at render time.
217 $total_qty = (float) array_sum( array_column( $lines, 'qty' ) );
218 $line_count = \count( $lines );
219
220 $tax_total = $amounts['tax_total'];
221 $total = $amounts['total'];
222 $total_excl = $total - $tax_total;
223 $refund_total = $amounts['refund_total'];
224 // Templates render the customer-facing balance after a partial refund.
225 // Stays at 0 when nothing was refunded so detailed-receipt's section
226 // guard `{{#totals.net_total}}…{{/totals.net_total}}` collapses.
227 $net_total = $refund_total > 0 ? max( 0.0, $total - $refund_total ) : 0.0;
228
229 return array(
230 'subtotal' => $display_incl ? $subtotal_incl : $subtotal_excl,
231 'subtotal_incl' => $subtotal_incl,
232 'subtotal_excl' => $subtotal_excl,
233 'discount_total' => $display_incl ? $amounts['discount_total']['incl'] : $amounts['discount_total']['excl'],
234 'discount_total_incl' => $amounts['discount_total']['incl'],
235 'discount_total_excl' => $amounts['discount_total']['excl'],
236 'sale_savings_total' => $sale_savings_totals[ $display_basis ],
237 'sale_savings_total_incl' => $sale_savings_totals['incl'],
238 'sale_savings_total_excl' => $sale_savings_totals['excl'],
239 'total_saved' => $total_saved[ $display_basis ],
240 'total_saved_incl' => $total_saved['incl'],
241 'total_saved_excl' => $total_saved['excl'],
242 'total_saved_complete' => $savings_complete[ $display_basis ],
243 'tax_total' => $tax_total,
244 'total' => $display_incl ? $total : $total_excl,
245 'total_incl' => $total,
246 'total_excl' => $total_excl,
247 'paid_total' => $amounts['paid_total'],
248 'change_total' => $amounts['change_total'],
249 'refund_total' => $refund_total,
250 'net_total' => $net_total,
251 'total_qty' => $total_qty,
252 'line_count' => $line_count,
253 );
254 }
255
256 /**
257 * Attach labels: explicit label → scoped type key → scoped other fallback.
258 *
259 * @param array $tax_ids Tax ID rows.
260 * @param string $scope Customer or store.
261 * @param string $locale Receipt locale.
262 * @return array
263 */
264 public static function label_tax_ids( array $tax_ids, string $scope, string $locale = '' ): array {
265 $labels = Receipt_I18n_Labels::get_labels( $locale );
266 $prefix = $scope . '_tax_id_label_';
267
268 return array_map(
269 static function ( array $tax_id ) use ( $labels, $prefix ): array {
270 if ( ! empty( $tax_id['label'] ) ) {
271 return $tax_id;
272 }
273
274 $type = isset( $tax_id['type'] ) ? (string) $tax_id['type'] : 'other';
275 $key = $prefix . $type;
276 $tax_id['label'] = $labels[ $key ] ?? $labels[ $prefix . 'other' ];
277
278 return $tax_id;
279 },
280 $tax_ids
281 );
282 }
283
284 /**
285 * Resolve non-empty variation attributes to tag-free label/value pairs.
286 *
287 * @param \WC_Product_Variation $variation Variation product.
288 * @return array
289 */
290 public static function variation_attribute_pairs( \WC_Product_Variation $variation ): array {
291 $pairs = array();
292 foreach ( $variation->get_variation_attributes() as $attribute_key => $attribute_value ) {
293 if ( '' === (string) $attribute_value ) {
294 continue;
295 }
296
297 $taxonomy = preg_replace( '/^attribute_/', '', (string) $attribute_key );
298 $value = $variation->get_attribute( $taxonomy );
299 $pairs[] = array(
300 'key' => wp_strip_all_tags( wc_attribute_label( $taxonomy, $variation ) ),
301 'value' => wp_strip_all_tags( '' !== $value ? $value : (string) $attribute_value ),
302 );
303 }
304
305 return $pairs;
306 }
307
308 /**
309 * Map a basis pair to its display value and two explicit values.
310 *
311 * @param string $key Bare field name.
312 * @param array $pair Nullable incl/excl amounts.
313 * @param bool $display_incl Whether the bare amount includes tax.
314 * @return array
315 */
316 private static function triple( string $key, array $pair, bool $display_incl ): array {
317 return array(
318 $key => $pair[ $display_incl ? 'incl' : 'excl' ],
319 $key . '_incl' => $pair['incl'],
320 $key . '_excl' => $pair['excl'],
321 );
322 }
323 }
324