This repository was archived by the owner on Jul 10, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathx.class
More file actions
575 lines (494 loc) · 24.3 KB
/
x.class
File metadata and controls
575 lines (494 loc) · 24.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
<?php
namespace CG;
use CG as r;
use CG\DB as db;
use CG\Backend as be;
use CG\Util as u;
/**
* @file
* rCredits Transaction class (and extension MyX, which looks at the transaction from one party's view)
*/
define('TX_FLDS', 'serial type amount goods payer payee payerAgent payeeAgent for flags created');
class X {
var $ray0; // an assoc of all the transaction's fields (including the data field, still serialized)
/**
* Instantiate (create or retrieve) a transaction record
* Call by:
* new X(info) [saves a new transaction] OR
* x(xid) (see the x() function below)
* @param int xid: the record ID
* @param assoc $info: initial field values for the transaction, to be created and saved in the database
* @param bool $save: <save the record in the database>
*/
function __construct($xid = [], $save = TRUE) {
if (is_array($info = $xid)) { // referring to existing db record
$this->ray0 = $this->createNew($info, $save); // create new record
} else $this->ray0 = db\get('*', 'r_txs', 'xid=:xid', compact('xid'));
}
/**
* Return the value of the specified field that is not a simple property of the class.
* @param string $field: name of field value to get
* @return the value if it exists, else NULL
*/
public function __get($f) {
$x = $this;
$res = @$x->ray0[$f];
if (u\inList($f, TX_FLAGS)) return u\getBit($x->flags, $f);
if ($f == 'data') return $res ? unserialize($res) : [];
// if ($f == 'ray') return $x->ray0;
return $res;
}
/**
* Create a new transaction record set in the database (main record plus optional related records).
* @param assoc $info: array of field values for new transaction
* amount: (required) how much to transfer
* payer, payee: (required) uids
* for: purpose of the transaction OR a simple array (payer's purpose, payee's purpose)
* etc.
* @param bool $save: <save the record in the database>
* @return the record assoc (FALSE if saving fails)
*/
private function createNew($info, $save) {
extract($info);
if (@$data and !is_array($data)) $data = unserialize($data);
if ($data2 = just(TX_DATA_FLDS, $info)) foreach ($data2 as $k => $zot) unset($info[$k]);
$info['data'] = (@$data ?: []) + (@$data2 ?: []);
$xray = $this->setup($info);
if (!$save) return $xray;
$DBTX = db_transaction();
if ($xray = $this->save($xray)) $xrays[] = $xray; else {$DBTX->rollback(); return FALSE;}
if (!$this->makeExtraTxs($xrays) or !$this->cacheBothTotals($xrays)) {$DBTX->rollback(); return FALSE;}
unset($DBTX); // commit
return $xrays[0]; // xrays[0] not xray because it might have changed
}
/**
* Save the transaction
* @param assoc $xray: transaction info.
* amount: (required) how much to transfer
* payer, payee: (required) uids
* for: purpose of the transaction OR a simple array (payer's purpose, payee's purpose)
* etc.
* (serial field is always included for subsidiary transactions)
* NOTE: This function should be called only within a \db_transaction()
* @return the transaction array, with extra info added (FALSE if the save fails)
*/
private function save($xray) {
if (!$xid = db\insert('r_txs', $xray)) return FALSE;
if (!@$xray['serial'] or @$xray['rewards']['xfee']) { // main and fee transactions need some touching up
extract(just('payer payee', $xray));
$payerTid = $payer > 0 ? r\xid2tid($xid, $payer) : 0;
$payeeTid = $payee > 0 ? r\xid2tid($xid, $payee) : 0;
if (!@$xray['serial']) $serial = $xid; // set serial for this (main transaction) and remember it for dependents
$info = compact(ray('xid serial payerTid payeeTid'));
if (!db\update('r_txs', $info, 'xid')) return FALSE;
} else $info = [];
return $info + $xray;
}
/**
* Add rebate and bonus or exchange fees to the transaction set, if appropriate.
* @param array $xrays: array of related transaction arrays
* @return TRUE if success else FALSE
*/
private function makeExtraTxs(&$xrays) {
extract(just('type serial goods payer payee data payerTid payeeTid created payeeFor', $xrays[0]));
if ($type != TX_TRANSFER) return TRUE;
$data = unserialize($data);
if ($amount = @$data['xfee']) {
list ($type, $goods, $payerAgent, $payeeAgent, $taking) = array(TX_XFEE, FOR_USD, $payer, $payee, TRUE);
$for = ($amount == R_XFEE_CHECK and !strpos($payeeFor, BY_CARD)) ? t('check fee') : t('card fee');
if ($xray = $this->save($this->setup(compact(ray(TX_FLDS))))) {
$xrays[] = $xray;
} else return FALSE;
}
if ($amount = -@$data['coupon']) {
list ($goods, $payerAgent, $payeeAgent, $taking) = array(FOR_USD, $payer, $payee, TRUE);
$for = t('discount rebate (on #%tid)');
$for = [tr($for, 'tid', $payerTid), tr($for, 'tid', $payeeTid)];
if ($xray = $this->save($this->setup(compact(ray(TX_FLDS))))) {
$xrays[] = $xray;
} else return FALSE;
}
return TRUE;
}
/*
private function rewardSetup($serial, $type, $payee, $amount, $for, $created) {
$payer = $payerAgent = r\acct($payee)->community;
$payeeAgent = $payee;
$goods = FOR_USD; // not actually for USD, but certainly not for goods or non-goods, since not an exchange
return $this->setup(compact(ray(TX_FLDS)));
}
*/
/**
* Create a new transaction and set up all the important fields.
* @param assoc $info: transaction info.
* amount: (required) how much to transfer
* payer, payee: (required) uids
* for: purpose of the transaction OR a simple array (payer's purpose, payee's purpose)
* etc.
* @return the array, with some extra field information added
*/
private function setup($info) {
global $channel, $boxUser; // through what medium was the transaction requested (web, SMS, etc.), what machine
// $fields = 'type data flags goods amount payer payee payerFor payerAgent payeeAgent created rebate bonus';
// $fields = 'type data flags goods amount payer payee payerReward payeeReward payerFor payerAgent payeeAgent created';
$fields = 'type data flags goods amount payer payee payerFor payerAgent payeeAgent created';
extract(just($fields . ' for ' . TX_FLAGS, $info)); // flags including offline, short, etc.
u\setDft($type, TX_TRANSFER);
u\EXPECT(compact(ray('amount payer payee')), 'float zid zid|empty');
if ($type != TX_BANK) u\EXPECT($payer and $payee and $payer != $payee, "null or self trading -- payer=$payer payee=$payee");
// highly inefficient list ($a1, $a2) = array(r\acct($payer), r\acct($payee));
// u\EXPECT($a1->ok and $a2->ok, 'non-member in tx' . $specs);
u\setDft($goods, FOR_USD);
u\setDft($payerAgent, $payer);
u\setDft($payeeAgent, $payee);
u\setDft($flags, 0);
u\setDft($created, r\rTime());
u\setDft($data, []);
if ($type != TX_BANK and !isset($payerFor)) {
u\setDft($for, ray(R_WHYS)[$goods]);
list ($payerFor, $payeeFor) = is_array($for) ? $for : [$for, $for];
unset($info['for']);
}
$goods += 0;
$box = @$boxUser ?: 0;
// if (r\isGiveback($payee, $payer, @$data)) $type = TX_GIVEBACK;
// if (!isset($payerReward) and !isset($payeeReward)) extract($this->getRewards($info)); // payerReward and payeeReward
$data = $this->dataSetup($payer, $payee, $amount, $goods, @$payerFor, @$data, $channel);
extract(just(TX_FLAGS, $data));
if (@$undoneBy) $undone = TRUE;
foreach (ray(TX_FLAGS) as $k) {
if (@$$k) u\setBit($flags, $k);
unset($info[$k]);
if ($k != 'undoes') unset($data[$k]); // data['undoes'] holds tid, so don't zap
}
$data = $data ? serialize($data) : '';
// if (isGAME and $payer > 0) $info += ['members' => $a1->members];
return compact(ray($fields . ' payeeFor channel box')) + $info;
}
/**
* Return the appropriate reward amounts for the given data.
*//*
private static function getRewards($info) {
extract(just('payer payee amount goods', $info));
$fields = 'payerReward payeeReward';
if (@$goods != FOR_GOODS or r\owning($payer, $payee)) {
return ray($fields, 0, 0); // no rewards for an incestuous tx
} else return ray($fields, r\reward($payer, $payee, $amount, TRUE), r\reward($payer, $payee, $amount, FALSE));
}*/
/**
* Set up and return the data field for a transaction.
* @param assoc $data: forced values for xfees, if any
* $param int $channel: passed here in case we are reversing or editing a transaction on a different channel
* @return the revised $data, as an array
*/
private function dataSetup($payer, $payee, $amount, $goods, $for, $data, $channel) {
u\EXPECT(is_array($data), 'data should be array');
if ($goods == FOR_USD and !isset($data['xfee']) and $channel == TX_POS) {
$xfee = r\xfee($for, $amount);
if ($xfee > 0) $data += compact('xfee');
}
unset($data['preapproved']); // don't need these (but keep 'undoes', if any)
return $data;
}
/**
* Cache what we can
* @return <success>
*/
private function cacheBothTotals($xrays) {
foreach(ray('payer payee') as $k) if (!$this->cacheTotals($xrays[0][$k], $xrays)) return FALSE;
return TRUE;
}
/**
* Keep track of r, rewards, and committed amounts in the account record
* None of these figures is authoritative except 'committed'.
* r and rewards are the sum of the relevant fields in transactions.
* @return <success>
*/
private function cacheTotals($uid, $xrays) {
global $channel;
// extract(just('amount type xid payer payee payerReward payeeReward created data', $xrays[0]), EXTR_PREFIX_ALL, 't');
extract(just('amount type xid payer payee created data', $xrays[0]), EXTR_PREFIX_ALL, 't');
// if ($t_state < TX_DONE) return; // don't change totals if transaction doesn't count (yet)
// if ($uid <= 0) return TRUE; // cache only usd for community and region
if (!$a = r\acct($uid)) return FALSE;
// extract((array) $a->account($cacheFields = 'balance rewards committed lastTx access'));
extract((array) $a->account($cacheFields = 'balance committed lastTx access'));
if ($channel != TX_CRON) $access = r\rTime(); // track last transaction or login (just major access)
$toMe = $uid == $t_payee ? -1 : 1;
if ($t_payer > 0) $lastTx = array($t_xid, $toMe * ($t_payee - $t_payer), round($toMe * $t_amount, 2), $t_created);
// $reward = $uid == $t_payee ? @$t_payeeReward : @$t_payerReward;
// $rewards = round($rewards + $reward, 2);
/// debug(compact(ray('uid xrays rewards reward toMe')));
foreach ($xrays as $xray) {
extract(just('payer payee amount type', $xray));
if (!in($uid, [$payer, $payee])) continue;
if ($payer == $uid) $amount = -$amount;
$balance = round($balance + $amount, 2);
/// debug(compact('uid','r','balance'));
// $a->slaveCacheR($r); // don't let slave r go negative
// $a->slaveCacheR($balance); // don't let slave balance go negative?
// if (r\isReward($type)) {
// if (in_array($type, [TX_REBATE, TX_BONUS])) $committed = round($committed + $amount * $a->share / 100, 2);
// }
}
// handle special case of paying the community to "give back" rewards [see also w\transfer()]
// if (r\isGiveback($uid, $t_payer, @$t_data)) $cache->rewards += $amount; // amount<0
return $a->update(compact(ray($cacheFields)));
// NO if ($t_type == TX_TRANSFER and $t_amount > 0) r\membershipEvent($a, 'bona', TRUE);
}
/**
* Update the saved record in the database with the given data.
* Call by:
* update($info, $just); OR
* update($fields, $value1, ...)
* @param assoc $info: data to update
* @param string $just: update only these fields (update all if NULL, none if '')
* @param string $fields: space-delimited list of field names
* @param mixed $value1 (etc): new values for those fields
* @return: TRUE if update succeeds, FALSE if update fails
*/
public function update($info, $just = NULL) {
$x = $this;
if (!is_array($info)) { // second syntax
$args = func_get_args();
$keys = ray(array_shift($args));
u\EXPECT(count($keys) == count($args), 'assoc arg count mismatch');
return $x->update(array_combine($keys, $args));
}
if (isset($just)) $info = just($just, $info);
if (isset($info['data'])) { // allow setting data to empty
$info['data'] = ($data = $info['data']) ? serialize($data) : '';
} elseif ($data = just(TX_DATA_FLDS, $info)) $info['data'] = serialize($data + ($x->data ?: []));
if ($flagFields = just(TX_FLAGS, $info)) {
$flags = isset($info['flags']) ? $info['flags'] : $x->flags;
foreach ($flagFields as $k => $v) {
u\setBit($flags, u\consta('b', $k), $v);
unset($info[$k]);
}
}
//NO! makes themX fail! foreach ($info as $k => $v) $x->ray0[$k] = $v; // update internal cache
$info['xid'] = $x->xid; // make sure db\update gets this
return db\update('r_txs', $info, 'xid');
}
/**
* Set a field of an old transaction for the user and update their cache.
* @param assoc $toChange: $fieldName => $newValue
* $fieldName: the field to update
* $newValue: what to update it to
* NOTE: tx data['changes'][time agent][] = [field, oldValue] (this gives a complete history of the transaction)
* (prefixes added to the payerFor or payeeFor fields when changing state will not be included in changes)
*/
public function setFields($toChange) {
$x = $this; $xid = $x->xid; $xray = $x->ray0;
global $mya;
// extract(just('data payer payee amount goods payerFor payeeFor payerReward payeeReward created channel', $xray));
extract(just('data payer payee amount goods payerFor payeeFor created channel', $xray));
extract($toChange, EXTR_PREFIX_ALL, 'ch');
$chKeys = array_keys($toChange);
$dataRay = unserialize($data);
$DBTX = \db_transaction();
if (isset($ch_goods) or isset($ch_amount)) { // financial info changed
// foreach (ray('amount payerReward payeeReward') as $k) $xrayNeg[$k] = -$$k; // negate the transaction and rewards amounts
$xrayNeg['amount'] = -$amount;
$x->cacheBothTotals([$xrayNeg + $xray]); // subtract old values from cache
// $toChange += $this->getRewards($toChange + $xray);
$x->cacheBothTotals([$toChange + $xray]); // cache values for the revised transaction
}
$agt = $mya->proSe ? '' : (' ' . $mya->agentA->mainQid); // don't record agent if proSe
$dataRay['changes'][r\rTime() . $agt] = just($chKeys, $xray);
$toChange['data'] = $dataRay;
$x->update($toChange);
unset($DBTX); // commit
}
/**
* Set the bit in the given integer bit collection field to the given value.
* @param int $bit: number of bit to set (0 - 31)
* @param bool $on: whether to set it ON or OFF
*/
private function setBitx($bit, $on = TRUE, $field = 'risks') {
$n = $this->$field;
u\setBit($n, $bit, $on);
$this->update($field, $n);
}
public function risk($bit) {return u\getBit($this->risks, $bit);}
public function setRisk($bit, $on = TRUE) {$this->setBitx($bit, $on, 'risks');}
} // end of class x
class MyX extends x {
var $a;
function __construct($xid = [], $a = 0, $save = TRUE) {
parent::__construct($xid, $save);
$this->a = $a;
}
public function __get($f) {
$x = $this;
if ($f == 'ray') {
u\EXPECT((bool) $x->ray0, 'no xray!');
foreach (ray('toMe byMe tid otherTid otherUid taking') as $k) $extras[$k] = $x->$k;
return $x->ray0 + $extras;
}
// NO! Makes ...2 fields fail if tx is created from ->ray (as in themX) if (isset($x->ray0[$f])) return parent::__get($f);
foreach (ray('a xid banking toMe amount') as $k) $$k = @$x->$k;
foreach (['amount', 'xid'] as $k) $$k += 0;
if (substr($f, -1, 1) == '2' and $f = str_replace('2', '', $f)) return $banking ? '' : $x->themX->$f;
if (strpos($f, 'Purpose') and $f = str_replace('Purpose', 'For', $f)) return $x->$f;
if ($f == 'banking') return ($x->type == TX_BANK);
if ($f == 'toMe') return $banking ? ($amount < 0 xor $xid < 0) : $a->isMe($x->payee);
if ($f == 'fromMe') return $banking ? !$toMe : $a->isMe($x->payer);
if ($f == 'byMe') return $banking ?: ($x->taking xor !$toMe);
if ($f == 'reallyToMe') return ($toMe xor $amount < 0);
if ($f == 'name') return $a->fullName;
if ($f == 'for' or $f == 'purpose') return $banking // ?
? ($xid < 0 ? t('bank transfer failed') : ($amount < 0 ? t('from bank') : t('to bank')))
: ($toMe ? $x->payeeFor : $x->payerFor);
if ($f == 'agent') return $banking // ?
? ($a->co ? '' : $x->payer)
: ($toMe ? $x->payeeAgent : $x->payerAgent);
if ($f == 'tid') return ($banking or $a->id < 0) ? $xid : ($toMe ? $x->payeeTid : $x->payerTid);
if ($f == 'otherTid') return ($banking or $a->id < 0) ? $xid : ($toMe ? $x->payerTid : $x->payeeTid);
if ($f == 'them' or $f == 'otherUid') return $banking ? 0 : ($toMe ? $x->payer : $x->payee);
if ($f == 'themAgent') return $banking ? 0 : ($toMe ? $x->payerAgent : $x->payeeAgent);
if ($f == 'themX') return r\x($x->ray, r\acct($x->them, $x->themAgent));
return parent::__get($f);
}
/**
* Return an appropriate message about the transaction
* @param string $type: what type of message to return (actor, youDid, theyDid, or reverse)
* @param string $desc: how much of the description to include: none (''), vague, or full
* @param bool $withDate: <include the transaction date in the message>
* @return the message
*/
public function msg($msgType, $desc = '', $withDate = FALSE) {
$x = $this;
foreach (ray('toMe byMe') as $k) $extras[$k] = $x->$k;
$extras['name'] = r\acct($x->them)->fullName; // r\acct($toMe ? $x->payer : $x->payee)->fullName;
$extras['description'] = $x->toMe ? $x->payeeFor : $x->payerFor;
return self::msg0($x->ray + $extras, $msgType, $desc, $withDate);
}
public static function msg0($tx, $msgType, $purpose = '', $withDate = FALSE) {
extract(just('name amount toMe byMe goods description created', $tx));
$youDid = t('paid,received from,received from,repaid,paid,took back from,charged,refunded');
$theyDid = t('charged,refunded,paid,took back from,received from,repaid,paid,received from');
$reverse = t('request a refund of this charge,reverse this refund,refund this payment,request a reversal of this charge,request a refund of this payment,reverse this refund charge,reverse this charge,request a reversal of this refund');
$them = $name;
$neg = (int) ($amount < 0);
$index = 4 * $byMe + 2 * $toMe + $neg;
if ($msgType == 'actor') $msgType = $byMe ? 'youDid' : 'theyDid';
$types = explode(',', $$msgType);
$action = $types[$index];
if ($msgType == 'reverse') {
if ($goods == FOR_USD) {
return in($index, [0,3,4,7]) ? t('request a reversal of this charge') : t('reverse this exchange');
} else return $action;
}
$msg = $msgType == 'youDid' ? t('You') . ' %action %them' : ('%them %action ' . t('you'));
$msg .= ' %amount';
if ($purpose) {
if ($purpose == 'full') $purpose = '"' . $description . '"';
if ($purpose == 'vague') $purpose = obj(R_WHYS)->$goods;
$msg .= '|purpose';
}
if ($withDate) $msg .= '|when';
list ($amount, $created) = [u\fmtAmt($amount), u\fmtDate($created)];
return t($msg, compact(ray('action them amount purpose created')));
}
/**
* Format and return args for reporting a transaction (old or new) to the current account.
* Not all returned args are used for any particular type of transaction.
* Note that templates and tests depend on the names of some of these fields
* @param acct $a: the account to report to (usually the current account)
* @param array $tx: the transaction array (including xid)
* @param int $getBalance: 0=don't get any balances, 1=get my balance and tid, 2=get other party's balance and tid also
* @param float $shortfall: how much short my balance is, for a transaction (if any)
* @return assoc array of args
*/
public function reportArgs($getMyBalance = FALSE, $shortfall = 0) {
global $channel;
$x1 = $this;
$a1 = $x1->a;
$x2 = $x1->themX;
$a2 = r\acct($otherUid = $x1->them);
$tx = $x1->ray0;
// $fields = 'data amount goods payer payee payerReward payeeReward toMe payerPurpose payeePurpose payerTid payeeTid created';
$fields = 'data amount goods payer payee toMe payerPurpose payeePurpose payerTid payeeTid created';
foreach (ray($fields) as $k) $$k = $x1->$k;
// extract(just('rebate bonus', $data));
$reverses = isset($data['undoes']);
$role = $toMe ? 'payee' : 'payer';
u\both($tx, 'purpose', $toMe, $payeePurpose, $payerPurpose);
u\both($tx, 'tid', $toMe, @$payeeTid, @$payerTid); // no tid if unconfirmed
$tofrom = $toMe ? t('from') : t('to');
// $confirmAction = $toMe ? t('charge') : ($channel == TX_POS ? t('credit') : t('pay'));
$why = ray(R_WHYS)[$goods];
If ($amount < 0) { // for POS
$amount = -$amount; // rephrase as positive
$tofrom = t('to');
// $confirmAction = $toMe ? t('credit') : t('charge');
u\both($tx, 'did', $toMe, $goods == FOR_GOODS ? t('refunded') : t('credited'), t('charged'));
// if (r\isGiveback($payee, $payer, $data)) list ($tx['did'], $a2, $why) = [t('gave'), $a1, t('returning rewards to the community')];
} else u\both($tx, 'did', $toMe, $reverses ? t('re-charged') : t('charged'), (($channel == TX_POS and $a1->co) ? t('credited') : t('paid')));
/// debug(compact('tx','toMe','amount','reverses','goods'));
$amount = u\fmtAmt($amount);
$short = u\fmtAmt($shortfall);
if ($getMyBalance) {
if ($a1->can(B_BUY)) $balance = u\fmtAmt($a1->avail(FOR_GOODS));
}
/* if ($goods == FOR_GOODS) {
// u\both($tx, 'rewardType', $toMe, t('bonus'), t('rebate'));
// u\both($tx, 'rewardType', $toMe, t('reward'), t('reward'));
// u\both($tx, 'rewardAmount', $toMe, u\fmtAmt(@$payeeReward + 0), u\fmtAmt(@$payerReward + 0));
}
*/
$myName = $a1->fullName;
$otherName = $a2->fullName;
$created = u\fmtDate($created0 = $created);
$success = TRUE;
foreach (ray('r payer payee') as $one) unset($tx[$one]); // else preempts all subs that start thus
return compact(ray('created created0 xid role why payerPurpose payeePurpose toMe amount tofrom balance otherUid myName otherName short success')) + $tx;
}
/**
* Return the last transaction meeting the given criteria, for the given user.
* Rebates, bonuses, and deleted transactions are ignored
* @param string $where: the criteria (defaults to 1 = any)
* @param array $subs: optional substitutions (will be prefixed with colons)
* @return associative array: the old transaction record (FALSE if none)
*/
static function lastTx($a, $where = 1, $subs = [], $cache = FALSE) {
$basic = "$a->id IN (payer, payee) AND type>=0"; // make sure it's for this account
$sql = "SELECT * FROM r_txs WHERE ($basic) AND ($where) ORDER BY xid DESC LIMIT 1";
/// debug(compact('where','subs','myid','basic','sql'));
if (!$result = db\q($sql, $subs)->fetchAssoc()) return FALSE;
return r\x($result, $a);
}
/**
* Determine whether the record can be undone
* @param assoc $subs: (RETURNED) necessary substitutions, if any, including whatever was already there
* @return string: the error message (FALSE if the current agent CAN undo it on behalf of the current account)
*/
function nonoUndo(&$subs = []) {
$x = $this;
// global $mya;
// extract(just('amount toMe data payer payee payerTid payeeTid taking', $oldRecord));
// $data = unserialize($data);
if (@$x->data['undoneBy']) {
$subs['oldTid'] = $x->toMe ? $x->payeeTid : $x->payerTid;
return 'already undone';
}
$a1 = r\acct($x->toMe ? $x->payee : $x->payer, $x->a->agentId);
$a2 = r\acct($x->toMe ? $x->payer : $x->payee); // second account's permissions are irrelevant
if ($err = r\txPermErr($a1, $a2, $x->toMe, $x->toMe xor $x->amount < 0)) return $err;
return FALSE;
}
}
/**
* Return an existing transaction object or MyX.
* Call by:
* x(info, a) OR x(xid, a) [returns a MyX object] OR
* x(info) OR x(xid)
* @param int $xid: transaction record ID
* @param int $a: associated account
* @param assoc $info: initial field values for the transaction, to be created and saved in the database
*/
function x($xid, $a = '') {
$x = $a ? new MyX($xid, $a, FALSE) : new X($xid, FALSE);
return @$x->xid ? $x : FALSE;
}