-
Notifications
You must be signed in to change notification settings - Fork 0
/
Notes.txt
464 lines (353 loc) · 22.5 KB
/
Notes.txt
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
Found the Admin window module in includes\extmodules\tax_classes
Copied these files [see below] to a new folder: includes\extmodules\State_ZIPs
main.php
tax_classes_dialog.php
tax_classes_grid.php
tax_classes_main_panel.php
tax_rates_dialog.php
tax_rates_grid.php
For identity's sake, and the posibility that TomatoCart uses naming conventions for identifiers, I renamed these files as such:
main.php
State_ZIPs_dialog.php
State_ZIPs_grid.php
State_ZIPs_main_panel.php
ZIP_rates_dialog.php
ZIP_rates_grid.php
Added define('TABLE_STATE_ZIPS', DB_TABLE_PREFIX . 'state_zips'); to store\includes\database_tables.php
Started by opening main.php and identifying key elements that would need to be changed for our new dialog:
Ext.namespace("Toc.tax_classes"); -> Ext.namespace("Toc.State_ZIPs");
Changed all of the include filenames to match our new names listed above.
Ext.override(TocDesktop.TaxClassesWindow, { -> Ext.override(TocDesktop.StateZIPsWindow, {
var win = desktop.getWindow('tax_classes-win'); -> var win = desktop.getWindow('State_ZIPs-win');
pnl = new Toc.tax_classes.TaxClassesMainPanel({owner: this}); -> pnl = new Toc.State_ZIPs.StateZIPsMainPanel({owner: this});
id: 'tax_classes-win', -> id: 'State_ZIPs-win',
iconCls: 'icon-tax_classes-win', -> iconCls: 'icon-State_ZIPs-win',
createTaxClassesDialog: function() { -> createStateZIPsDialog: function() {
var dlg = desktop.getWindow('tax-class-dialog-win'); -> var dlg = desktop.getWindow('State-ZIPs-dialog-win');
dlg = desktop.createWindow({}, Toc.tax_classes.TaxClassesDialog); -> dlg = desktop.createWindow({}, Toc.State_ZIPs.StateZIPsDialog);
createTaxRatesDialog : function() { -> createZIPRatesDialog : function() {
var dlg = desktop.getWindow('tax-rate-dialog-win'); -> var dlg = desktop.getWindow('ZIP-rates-dialog-win');
dlg = desktop.createWindow({},Toc.tax_classes.TaxRatesDialog); -> dlg = desktop.createWindow({},Toc.State_ZIPs.ZIPRatesDialog);
Add module to TomatoCart:
Place self-named file in: includes\modules\access (used tax_classes.php in this directory as a reference)
Title File
includes\languages\en_US\modules\access
File Definitions:
\includes\langauges\en_US
Data Access Declarations:
\includes\jsons
ComboBox Information
The ComboBoxes are built with a "cbo" prefix, IE: cboCountries
The label for the combobox is derived from \includes\langugages\en_US IE: field_country = Country:
The call for this is in the PHP file: IE: <?php echo $osC_Language->get("field_country"); ?>
In this example, the combobox pulls from store: dsCountries
The store was built previously in the PHP files in the 'build' function
JSON_READER_ROOT == 'records'
JSON_READER_TOTAL == 'total'
jsons file is taking the database and returning it in json format in the 'encode($response)' form.
ComboBox populates data from 'store', example uses a store built previously 'dsCountries'
Here is the example coding from ExtJS website for a ComboBox:
// The data store containing the list of states
var states = Ext.create('Ext.data.Store', {
fields: ['abbr', 'name'],
data : [
{"abbr":"AL", "name":"Alabama"},
{"abbr":"AK", "name":"Alaska"},
{"abbr":"AZ", "name":"Arizona"}
]
});
// Create the combo box, attached to the states data store
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose State',
store: states,
queryMode: 'local',
displayField: 'name',
valueField: 'abbr',
renderTo: Ext.getBody()
});
We can use this to see how the data is visually formatted and help parse the TomatoCart code.
Here is a breakdown from ExtJS on how a data.Store is built:
// Set up a model to use in our Store
Ext.define('User', {
extend: 'Ext.data.Model',
fields: [
{name: 'firstName', type: 'string'},
{name: 'lastName', type: 'string'},
{name: 'age', type: 'int'},
{name: 'eyeColor', type: 'string'}
]
});
var myStore = Ext.create('Ext.data.Store', {
model: 'User',
proxy: {
type: 'ajax',
url: '/users.json',
reader: {
type: 'json',
rootProperty: 'users'
}
},
autoLoad: true
});
**** SOMETHING IN THE action: description is TomatoCart is removing the underscore, and capitalizing the first letter: IE: list_countries runs function(listCountries) ****
SOLVED: json.php uses:
$action = $_REQUEST['action'];
//process action
$words = explode('_', $action);
$action = $words[0];
if (sizeof($words) > 1) {
for($i = 1; $i < sizeof($words); $i++)
$action .= ucfirst($words[$i]);
}
This code is processing the original request (list_countries) and then converting it by breaking-up the string into individual words with the "_" deliminator and appending each word to the first using the "ucfirst" (UpperCase First) function to capitalize the first letters.
So, let's begin plotting the idea for our tool:
Dialog Box -> module: 'State_ZIPs', action: 'list_countries' -> Executes listCountries in jsons\State_ZIPs.php ->
Current function in zone_groups.php builds the record with country_id: 0, countries_name: 'all countries' first and then appends each entry from 'table_countries' to the record in the same format (country_id: [id], country_name: [name]) ->
Adds to dataStore. -> Build new comboBox using this dataStore as the 'store' value.
IMPORTANT: Make sure to add listener information
ICON: Had to add icon 'zips.png' to default template folder in images/icons/16x16 and 48x48. Must then modify three different .css files to get icon to appear in Admin console.
State ZIPs dialog grid will need to show: State | Country | | Base Sales Tax | Total # of Entries | Edit/Delete buttons
State: DB TABLE: toc_zones - State (hidden zone_id) get zone_country_id to pull Country
Country: DB_TABLE: toc_countries - search column countries_id for the zone_country_id pulled from the State entry.
Tax: Store/Get base sales tax for this state. (Can be 0% if none.)
Total: Will need to connect to linked table and count # of lines for state's zone_id. (Can be 0)
Edit/Delete: Associated action functions - will need to make sure these affect items from linked table too.
Table setup: toc_state_zips - "state_zip_id" [Integer / Auto Increment / Primary Key], "State_ID" [Integer], "Country_ID" [Integer], "Tax" [Decimal]
toc_zip_rates - "State_ID" [Integer], "ZIP" [Integer], "Name" [String], "Tax" [Decimal], "Total Tax" [Decimal] (Add to State Base Tax (if exists) <- Can use ZIP as unique ID for reference.
DEALING WITH TABLES in MySQL :: store\includes\database_tables.php stores all of the name references used in the codes for table references. Adding references for our new tables.
Trying to find out where $_REQUEST['geo_zone_id'] is being sent from in zone_groups.php to help with our code.
It must be sent from whatever calls the function it is in: listZoneEntries()
We know from previous code this is translated through the jsons file, so the call will be 'list_zone_entries'.
This is being called from zone_entries_grid.php from the Toc.zone_groups.ZoneEntriesGrid function. Passed in through the 'config' asset, perhaps? We'll see what's calling ZoneEntriesGrid...
Suspicious about 'geo_zone_entries' from zone_groups_main_panel.php... seeing where this cross-references.
Check for iniGrid(record) function sent in zone_groups_main_panel.php instead...
This is defined in zone_entries_grid.php...
Looks like it breaks down the record grabbed from the groups selection... geo_zone_id becomes this.geoZoneId, geo_zone_name becomes this.geoZoneName, then stores this.geoZoneId into the store.baseParams... in this example, store.baseParams['geo_zone_id'].
Assuming this is the id used to grab the record seen in $_REQUEST. Applying to State_ZIP grid functions to test...
DISCOVERED the trick: use the item you want to get in 'record.get' (I.E. 'State_ID') and the id for the dataSet in the baseParams that you are pulling from. (I.E. 'State_ZIP_id')
USE:
Source dataSet:
config.ds = new Ext.data.Store({
url: Toc.CONF.CONN_URL,
baseParams: {
module: 'State_ZIPs',
action: 'list_State_ZIPs'
},
reader: new Ext.data.JsonReader({
root: Toc.CONF.JSON_READER_ROOT,
totalProperty: Toc.CONF.JSON_READER_TOTAL_PROPERTY,
id: 'State_ZIP_id'
},[
'State_ID',
'State_ZIP_id',
'State_ZIP_title',
'State_country',
'State_base_tax',
'State_entry_count'
]),
autoLoad: true
});
iniGrid to pull the value we want:
iniGrid: function(record) {
this.StateZIPId = record.get('State_ID');
var store = this.getStore();
store.baseParams['State_ZIP_id'] = this.StateZIPId;
store.load();
},
SUCCESS: Window is now populating test data in the State area and the ZIP entry area.
State ZIPs area - Add / Edit / Delete fields complete.
Modified code so that ZIP rate Add/Edit/Delete and Batch Delete buttons all work.
Verified against live database that:
State Fields:
- Can successfully add Zones into State list with Country tagged and a base State Rate appended. Entry adds # of ZIPs listed for this item.
- Refresh button forces refresh of most current data.
- Selecting an entry forces a change in the ZIP Rates field with the applicable data for this state.
- Edit button puts selected state's data into the forced dialog box that opens.
- Saving an Edited State does not update the database, but writes a new line to database. [NEED TO FIX]
- Close closes the dialog.
- Delete button deletes the corresponding State along with any ZIP rate children that were created.
ZIP Rates:
- Add button creates Dialog to add ZIP Rates to the list of States given (already in State List ONLY).
- Delete (top row) deletes any checked ZIP Rates in the list.
- Refresh updates the entire box.
- Header bar checkbox selects all items in the list.
- Selecting an entry marks the checkbox.
- Edit button opens ZIP rate dialog from the database.
- Saving updates correct database entry and closes the dialog while updating the boxes for the most current information.
- Close closes the dialog and stops changes.
- Delete button erases the entry from the zip_rates database.
NOTES: Everything is finished, just need to fix the Edit dialog for the State ZIPs box.
Fixed issue with State field's Edit button not updating the correct entry in the database. This is now working properly.
WDF530PLYW0
CLIENT SIDE
Taxes are calculated using the order_total module. This module is on the store side and the admin side, so pay close attention when editing to make sure the correct file is being modified.
ajax_shopping_cart.php opens order_total
//order totals
$order_totals = array();
foreach ($osC_ShoppingCart->getOrderTotals() as $module) {
$order_totals[] = array('title' => $module['title'], 'text' => $module['text']);
}
$cart['orderTotals'] = $order_totals;
shopping_cart.php defines 'osC_OrderTotal', or suffix '_order_totals' as 'includes/classes/order_total.php
order_total.php loops through each module and checks to see if its enabled variable is set to "true". If so, runs it when calculating.
Gets: $this->_data[] = array('code' => $GLOBALS[$module]->getCode(),
'title' => $output['title'],
'text' => $output['text'],
'value' => $output['value'],
'tax' => ($post_total - $pre_total - $output['value']),
'sort_order' => $GLOBALS[$module]->getSortOrder());
shopping_cart.php puts this data into the $this->_order_totals variable. We should output this variable to see what the script is returning.
What is happening is essentially, [Get Customer Zone] -> [Is Tax Enabled?] -> [Get items in Tax Groups] -> [Get applicable tax for that item if customer's Zone is listed in that group] -> [add to total]
So, let's search for each specific step...
1.) Get Customer's Zone:
billing_address_details.php generates for user form for entering the billing address.
The specific drop-down used is named 'billing_state'.
'Continue' button calls the btnSaveBillingInformation function in includes\javascript\checkout.js which then executes this.btnSaveBillingInformationClick.
This action executes saveBillingAddress in includes\modules\jsons\checkout.php
If any zones exist for that state, checkout.php processes it...
Gets set as $data['state']
Sends $data to ShoppingCart->setRawBillingAddress($data)
Shopping cart assigns $data variables to the _billing_address variable.
Then calls function this->_calculate()
_calculate sets the tax as: $tax = $osC_Tax->getTaxRate($data['tax_class_id'], $this->getTaxingAddress('country_id'), $this->getTaxingAddress('zone_id'));
$tax_description = $osC_Tax->getTaxRateDescription($data['tax_class_id'], $this->getTaxingAddress('country_id'), $this->getTaxingAddress('zone_id'));
Data is first gathered back in shopping_cart.php in getTaxingAddress(array identifier you are seeking (I.E. 'state'))
getTaxingAddress('state') will return getBillingAddress('state'), or getShippingAddress('state') depending on options...
getBillingAddress will return this->_billing_address('state')
This successfully returns the address element we are looking for, in this case, the customer's zone - 'zone_id'
2.) Is Tax Enabled?: See above line: $tax = $osC_Tax->getTaxRate($data['tax_class_id'], $this->getTaxingAddress('country_id'), $this->getTaxingAddress('zone_id'));
The variable, $data['tax_class_id']
If a shopping cart item does not have a matching 'TAX CLASS', getTaxRate will return a $tax_rate of 0, or "no tax" (essentially).
This calls $osC_Currencies (inclues\classes\Currencies.php) addTaxRatetoPrice.
If tax_rate is greater than 0, it adds the amount and inserts a line for the tax, else, nothing is done, essentially disabling the tax.
3.) Get items in Tax Groups: If you reference the same 'getTaxRate' from above, the first 'tax_class_id' used to measure the tax gathers this information. Nothing more is needed.
4.) Get applicable tax The getTaxRate call from before does all of this by the class_id.
5.) Add to total The call to $osC_Currencies adds the tax line and amount to our order total.
Now we know how it works. We'll need to tailor all of this to work by ZIP instead!
Proposal:
Our module allows us to create an entirely new series of data based on ZIP codes that provide tax data.
This tax data is added on top of what we define as a "state base tax" by country/state.
Luckily, Currencies already does the math for us when adding multiple taxes. Let's do this:
billing_address_details already gathers the customer's ZIP.
Line 173 in checkout.php begins the gathering for POST_CODE and this gets saved into $data['postcode'] from $_REQUEST['billing_postcode']
This eventually gets sent to _caculate, this is where coding will need to begin to change.
getTaxRate (in tax.php) will need to gather all products that have a tax key that applies tax (this is setup per product).
It will then need to scan table_zip_rates for the provided ZIP code.
If found, we need to get the tax rate for this ZIP and then also get the base state tax for the state it belongs too (if any.)
If not found, we need to get the user's entered state and just apply the base state tax (if any).
If not, return 0.
Cataloged Changes:
LN: 1279, under: function _calculate, includes\classes\shopping_cart.php,
$tax = $osC_Tax->getTaxRate($data['tax_class_id'], $this->getTaxingAddress('country_id'), $this->getTaxingAddress('zone_id'));
$tax = $osC_Tax->getTaxRate($data['tax_class_id'], $this->getTaxingAddress('postcode'), $this->getTaxingAddress('zone_id'));
NOTES: This will pass our ZIP code to tax.php now.
LN: 23 , under: //class methods,
function getTaxRate($class_id, $country_id = -1, $zone_id = -1) {
function getTaxRate($class_id, $ZIP = -1, $zone_id = -1) {
LN: 26: if ( ($country_id == -1) && ($zone_id == -1) ) {
if ( ($ZIP == -1) && ($zone_id == -1) ) {
LN: 27: $country_id = $osC_ShoppingCart->getTaxingAddress('country_id');
$ZIP = $osC_ShoppingCart->getTaxingAddress('ZIP');
LN: 31: if (isset($this->tax_rates[$class_id][$country_id][$zone_id]['rate']) == false) {
if (isset($this->tax_rates[$class_id][$ZIP][$zone_id]['rate']) == false) {
LN: 32-47: REMOVE:
$Qtax = $osC_Database->query('select sum(tax_rate) as tax_rate from :table_tax_rates tr left join :table_zones_to_geo_zones za on (tr.tax_zone_id = za.geo_zone_id) left join :table_geo_zones tz on (tz.geo_zone_id = tr.tax_zone_id) where (za.zone_country_id is null or za.zone_country_id = 0 or za.zone_country_id = :zone_country_id) and (za.zone_id is null or za.zone_id = 0 or za.zone_id = :zone_id) and tr.tax_class_id = :tax_class_id group by tr.tax_priority');
$Qtax->bindTable(':table_tax_rates', TABLE_TAX_RATES);
$Qtax->bindTable(':table_zones_to_geo_zones', TABLE_ZONES_TO_GEO_ZONES);
$Qtax->bindTable(':table_geo_zones', TABLE_GEO_ZONES);
$Qtax->bindInt(':zone_country_id', $country_id);
$Qtax->bindInt(':zone_id', $zone_id);
$Qtax->bindInt(':tax_class_id', $class_id);
$Qtax->execute();
if ($Qtax->numberOfRows()) {
$tax_multiplier = 1.0;
while ($Qtax->next()) {
$tax_multiplier *= 1.0 + ($Qtax->value('tax_rate') / 100);
}
$tax_rate = ($tax_multiplier - 1.0) * 100;
LN: 32: INSERT:
var $ZIPs = false;
var $States = false;
var $Taxable = false;
$ZIPtax = $osC_Database->query('select Tax from :table_zip_rates WHERE ZIP = :ZIP');
$ZIPtax->bindTable(':table_zip_rates', TABLE_ZIP_RATES);
$ZIPtax->bindInt(':ZIP', $ZIP);
$ZIPtax->execute();
if ($ZIPtax->numberOfRows() > 0) {
$ZIPs = true;
}
$StateCheck = $osC_Database->query('select Tax from :table_state_zips WHERE State_ID = :zone_id');
$StateCheck->bindTable(':table_state_zips', TABLE_STATE_ZIPS);
$StateCheck->bindInt(':zone_id', $zone_id);
$StateCheck->execute();
if ($StateCheck->numberOfRows() > 0) {
$States = true;
}
if ($class_id > 0) {
$Taxable = true;
}
var $taxer = 0;
if ($ZIPs == true) {
$taxer = $taxer + $ZIPtax->value('Tax');
}
if ($States == true) {
$taxer = $taxer + $StateCheck->value('Tax');
}
if ($Taxable == true) {
$tax_rate = $taxer;
LN: 1280: includes\classes\shopping_cart.php
$tax_description = $osC_Tax->getTaxRateDescription($data['tax_class_id'], $this->getTaxingAddress('country_id'), $this->getTaxingAddress('zone_id'));
$tax_description = $osC_Tax->getTaxRateDescription($data['tax_class_id'], $this->getTaxingAddress('ZIP'), $this->getTaxingAddress('zone_id'));
LN: 58: includes\classes\tax.php
(LN 80 A/E) function getTaxRateDescription($class_id, $country_id, $zone_id) {
function getTaxRateDescription($class_id, $ZIP, $zone_id) {
LN: 61: includes\classes\tax.php
(LN 83 A/E) if (isset($this->tax_rates[$class_id][$country_id][$zone_id]['description']) == false) {
if (isset($this->tax_rates[$class_id][$ZIP][$zone_id]['description']) == false) {
LN: 32-69: REMOVE:
(LN 84-91 A/E) $Qtax = $osC_Database->query('select tax_description from :table_tax_rates tr left join :table_zones_to_geo_zones za on (tr.tax_zone_id = za.geo_zone_id) left join :table_geo_zones tz on (tz.geo_zone_id = tr.tax_zone_id) where (za.zone_country_id is null or za.zone_country_id = 0 or za.zone_country_id = :zone_country_id) and (za.zone_id is null or za.zone_id = 0 or za.zone_id = :zone_id) and tr.tax_class_id = :tax_class_id group by tr.tax_priority');
$Qtax->bindTable(':table_tax_rates', TABLE_TAX_RATES);
$Qtax->bindTable(':table_zones_to_geo_zones', TABLE_ZONES_TO_GEO_ZONES);
$Qtax->bindTable(':table_geo_zones', TABLE_GEO_ZONES);
$Qtax->bindInt(':zone_country_id', $country_id);
$Qtax->bindInt(':zone_id', $zone_id);
$Qtax->bindInt(':tax_class_id', $class_id);
$Qtax->execute();
LN: 62: INSERT:
(LN 84 A/E) $ZIPtax = $osC_Database->query('select Name from :table_zip_rates WHERE ZIP = :ZIP');
$ZIPtax->bindTable(':table_zip_rates', TABLE_ZIP_RATES);
$ZIPtax->bindInt(':ZIP', $ZIP);
$ZIPtax->execute();
ALL TAGS
AFTER EDIT NOW
LN: 89: includes\classes\tax.php
if ($Qtax->numberOfRows()) {
if ($ZIPtax->numberOfRows()) {
LN: 90: includes\classes\tax.php
$tax_description = '';
$zip_description = $ZIPtax->value('Name');
LN: 91-94: REMOVE:
while ($Qtax->next()) {
$tax_description .= $Qtax->value('tax_description') . ' + ';
}
LN: 88: INSERT:
$STATEabbrv = $osC_Database->query('select zone_code from :table_zones WHERE zone_id = :zone_id');
$STATEabbrv->bindTable(':table_zones', TABLE_ZONES);
$STATEabbrv->bindInt(':zone_id', $zone_id);
$STATEabbrv->execute();
$STATEtax = $osC_Database->query('select Tax from :table_state_zips WHERE State_ID = :zone_id');
$STATEtax->bindTable(':table_state_zips', TABLE_STATE_ZIPS);
$STATEtax->bindInt(':zone_id', $zone_id);
$STATEtax->execute();
if ($STATEtax->value('Tax') > 0) {
$state_description = $STATEabbrv->value('zone_code') . " State Tax";
$tax_description = $state_description;
LN: 104: INSERT:
if ($ZIPtax->numberOfRows()) {
$zip_description = $ZIPtax->value('Name');
if ($tax_description && !empty($tax_description)) {
$tax_description .= " + " . $zip_description . " Local Taxes";
} else {
$tax_description = $zip_description . " Local Sales Tax";
}
}