Skip to content
Draft
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c4695f6
Implement tax display values from Magento settings (#303)
Jade-GG Oct 18, 2023
ca4152e
Apply fixes from Prettier
royduin Oct 18, 2023
e7a6d11
Fix merge & implement latent feedback
Jade-GG Oct 18, 2023
c877353
Use price component everywhere, remove unnecessary calculation
Jade-GG Oct 18, 2023
959237d
simpleProduct as default
Jade-GG Oct 18, 2023
0573295
v-cloak on addtocart icons
Jade-GG Oct 18, 2023
2f49d60
Add tierprices
Jade-GG Oct 18, 2023
62a26fa
Apply fixes from Duster
Jade-GG Oct 18, 2023
539a3f2
Change customer token into cookie
indykoning Oct 18, 2023
b01db37
Apply fixes from Duster
indykoning Oct 18, 2023
3828e88
Safety checks on product options
Jade-GG Oct 19, 2023
898cdb2
Fix product options reactivity
Jade-GG Oct 19, 2023
4613d45
Apply fixes from Prettier
Jade-GG Oct 19, 2023
fb8d091
Calculate tax for product, implement price component in POP and search
indykoning Oct 19, 2023
4263d58
Apply fixes from Duster
indykoning Oct 19, 2023
1abbbc1
Restore index command
indykoning Oct 19, 2023
715ca93
Merge branch 'tax_display' of github.com:rapidez/core into tax_display
indykoning Oct 19, 2023
ecbd7e0
Apply fixes from Prettier
indykoning Oct 19, 2023
9d9b943
Restrict tierprices, respect more settings
indykoning Oct 20, 2023
f59d1bc
Apply fixes from Duster
indykoning Oct 20, 2023
bb6ee89
Improve codestyle
indykoning Oct 20, 2023
ce99948
Apply fixes from Duster
indykoning Oct 20, 2023
6cc9ec2
turn undefined into empty
indykoning Oct 20, 2023
60d4399
Merge branch 'tax_display' of github.com:rapidez/core into tax_display
indykoning Oct 20, 2023
f8ec3b7
Apply fixes from Prettier
indykoning Oct 20, 2023
5f19e9d
Fixes and cleanup
royduin Oct 24, 2023
20e6679
Apply fixes from Duster
royduin Oct 24, 2023
2b11f2f
Fix tier prices being strings making comparisons not work
Jade-GG Oct 25, 2023
7b61253
WIP - Price tests and some refactoring
royduin Oct 26, 2023
18ce4d6
Merge branch 'tax_display' of github.com:rapidez/core into tax_display
royduin Oct 26, 2023
43d3a57
Apply fixes from Duster
royduin Oct 26, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"php": "^8.0.2|^8.1",
"blade-ui-kit/blade-heroicons": "^2.0",
"cviebrock/laravel-elasticsearch": "^9.0|^10.0",
"gehrisandro/tailwind-merge-laravel": "^0.2.1",
"illuminate/database": "^9.0|^10.0",
"illuminate/events": "^9.0|^10.0",
"illuminate/queue": "^9.0|^10.0",
Expand Down
41 changes: 0 additions & 41 deletions resources/js/components/Product/AddToCart.vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,10 @@ export default {
adding: false,
added: false,
price: 0,
specialPrice: 0,
}),
mounted() {
this.qty = this.defaultQty
this.calculatePrices()
},
render() {
Expand Down Expand Up @@ -117,11 +113,6 @@ export default {
})
},
calculatePrices: function () {
this.price = parseFloat(this.simpleProduct.price) + this.priceAddition(this.simpleProduct.price)
this.specialPrice = parseFloat(this.simpleProduct.special_price) + this.priceAddition(this.simpleProduct.special_price)
},
getOptions: function (superAttributeCode) {
if (this.$root.swatches.hasOwnProperty(superAttributeCode)) {
let swatchOptions = this.$root.swatches[superAttributeCode].options
Expand All @@ -147,29 +138,6 @@ export default {
reader.onload = () => Vue.set(this.customOptions, optionId, 'FILE;' + file.name + ';' + reader.result)
reader.readAsDataURL(file)
},
priceAddition: function (basePrice) {
let addition = 0
Object.entries(this.customOptions).forEach(([key, val]) => {
if (!val) {
return
}
let option = this.product.options.find((option) => option.option_id == key)
let optionPrice = ['drop_down', 'radio'].includes(option.type)
? option.values.find((value) => value.option_type_id == val).price
: option.price
if (optionPrice.price_type == 'fixed') {
addition += parseFloat(optionPrice.price)
} else {
addition += (parseFloat(basePrice) * parseFloat(optionPrice.price)) / 100
}
})
return addition
},
},
computed: {
Expand Down Expand Up @@ -304,14 +272,5 @@ export default {
return disabledOptions
},
},
watch: {
customOptions: {
handler() {
this.calculatePrices()
},
deep: true,
},
},
}
</script>
110 changes: 110 additions & 0 deletions resources/js/components/Product/Price.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<script>
export default {
props: {
product: {
type: Object,
default: () => config.product || {},
},
location: {
type: String,
default: 'catalog',
},
options: {
type: Object,
default: {},
},
},

render() {
return this.$scopedSlots.default(this)
},

methods: {
getTaxPercent(product) {
let taxClass = product.tax_class_id ?? product
let taxValues = product.tax_values ?? window.config.tax.values[taxClass] ?? {}

// TODO: Figure out where to get the tax rate calculation from
let groupId = this.$root.user?.group_id ?? 0 // 0 is always the NOT_LOGGED_IN group
let customerTaxClass = window.config.tax.groups[groupId] ?? 0

return taxValues[customerTaxClass] ?? 0
},

calculatePrice(product, location, options = {}) {
let special_price = options.special_price ?? false
let displayTax = this.$root.includeTaxAt(location)

let price = special_price ? product.special_price ?? product.price ?? 0 : product.price ?? 0

if (options.product_options) {
price += this.calculateOptionsValue(price, product, options.product_options)
}

let taxMultiplier = this.getTaxPercent(product) + 1

if (window.config.tax.calculation.price_includes_tax == displayTax) {
return price
}

return displayTax ? price * taxMultiplier : price / taxMultiplier
},

calculateOptionsValue(basePrice, product, customOptions) {
let addition = 0

Object.entries(customOptions).forEach(([key, val]) => {
if (!val) {
return
}

let option = product.options.find((option) => option.option_id == key)
let optionPrice = ['drop_down', 'radio'].includes(option.type)
? option.values.find((value) => value.option_type_id == val).price
: option.price

if (optionPrice.price_type == 'fixed') {
addition += parseFloat(optionPrice.price)
} else {
addition += (parseFloat(basePrice) * parseFloat(optionPrice.price)) / 100
}
})

return addition
},
},

computed: {
specialPrice() {
return this.calculatePrice(this.product, this.location, Object.assign(this.options, { special_price: true }))
},

price() {
return this.calculatePrice(this.product, this.location, this.options)
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be thought out again, Magento doesn't work with prices this way.

Prices are calculated as follows: Final Price=Min(Regular(Base) Price, Group(Tier) Price, Special Price, Catalog Price Rule) + Sum(Min Price per each required custom option)

Note that prices are clamped to a minimum of 0 before and after the custom options are added. Also, the price gets rounded before getting multiplied by the qty.


isDiscounted() {
return this.specialPrice != this.price
},

range() {
if (!this.product.super_attributes) {
return null
}

let prices = Object.values(this.product.children).map((child) =>
this.calculatePrice(child, this.location, Object.assign(this.options, { special_price: true })),
)

return {
min: Math.min(...prices),
max: Math.max(...prices),
}
},

includesTax() {
return this.$root.includeTaxAt(this.location)
},
},
}
</script>
8 changes: 8 additions & 0 deletions resources/js/mixins.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,13 @@ Vue.mixin({
return await magento[method]('guest-carts/' + localStorage.mask + '/' + endpoint, data)
}
},

includeTaxAt(location) {
return location === true || location === false ? location : (window.config.tax.display[location] ?? 0) >= 1
},

decideTax(including, excluding, location) {
return this.includeTaxAt(location) ? including : excluding
},
},
})
3 changes: 3 additions & 0 deletions resources/js/vue-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ Vue.component('notifications', notifications)
import images from './components/Product/Images.vue'
Vue.component('images', images)

import price from './components/Product/Price.vue'
Vue.component('price', price)

Vue.component('autocomplete', () => import('./components/Search/Autocomplete.vue'))
Vue.component('login', () => import('./components/Checkout/Login.vue'))
Vue.component('listing', () => import('./components/Listing/Listing.vue'))
Expand Down
8 changes: 4 additions & 4 deletions resources/views/cart/overview.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@
</div>
</div>
<div class="flex items-center justify-between gap-5">
@{{ item.price | price }}
@{{ $root.decideTax(item.price, item.price_excl_tax, 'cart_price') | price }}
<div class="flex items-center gap-1">
<x-rapidez::input v-model="item.qty" v-bind:dusk="'qty-'+index" name="qty" class="w-14 px-1 text-center" type="number" :label="false" ::min="item.min_sale_qty > item.qty_increments ? item.min_sale_qty : item.qty_increments" ::step="item.qty_increments" />
<x-rapidez::button v-on:click="changeQty(item)" v-bind:dusk="'item-update-'+index" class="ml-1" :title="__('Update')">
<x-heroicon-s-arrow-path class="h-4 w-4" />
</x-rapidez::button>
</div>
@{{ item.total | price }}
@{{ $root.decideTax(item.total, item.total_excl_tax, 'cart_price') | price }}
</div>
</div>
<div class="mt-5 self-start">
Expand All @@ -48,11 +48,11 @@
<div class="flex w-full flex-wrap justify-end self-baseline lg:max-w-xs">
<dl class="mb-5 flex w-full flex-wrap rounded-lg border p-3 [&>dd:nth-last-of-type(-n+2)]:border-none [&>dd]:w-1/2 [&>dd]:border-b [&>dd]:py-3">
<dd>@lang('Subtotal')</dd>
<dd class="text-right">@{{ cart.subtotal | price }}</dd>
<dd class="text-right">@{{ $root.decideTax(cart.subtotal, cart.subtotal_excl_tax, 'cart_subtotal') | price }}</dd>
<dd v-if="cart.tax > 0">@lang('Tax')</dd>
<dd v-if="cart.tax > 0" class="text-right">@{{ cart.tax | price }}</dd>
<dd v-if="cart.shipping_amount > 0">@lang('Shipping')<br><small>@{{ cart.shipping_description }}</small></dd>
<dd v-if="cart.shipping_amount > 0" class="text-right">@{{ cart.shipping_amount | price }}</dd>
<dd v-if="cart.shipping_amount > 0" class="text-right">@{{ $root.decideTax(cart.shipping_amount, cart.shipping_amount_excl_tax, 'cart_shipping') | price }}</dd>
<dd v-if="cart.discount_name && cart.discount_amount < 0">@lang('Discount'): @{{ cart.discount_name }}</dd>
<dd v-if="!cart.discount_name && cart.discount_amount < 0">@lang('Discount')</dd>
<dd v-if="cart.discount_amount < 0" class="text-right">@{{ cart.discount_amount | price }}</dd>
Expand Down
29 changes: 29 additions & 0 deletions resources/views/components/price.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@props(['product' => 'simpleProduct', 'type' => 'catalog'])
@slots(['special'])

<price :product="{{ $product }}" type="{{ $type }}">
<div
{{ $attributes->twMerge('mt-1 flex items-center gap-2') }}
slot-scope="{ price, specialPrice, isDiscounted, range }"
>
<span v-text="$options.filters.price(specialPrice || price)" v-if="!range">
{{ $slot }}
</span>
<span
{{ $special->attributes->twMerge('text-13 font-normal line-through') }}
v-if="isDiscounted && !range"
v-text="$options.filters.price(price)"
>
{{ $special }}
</span>

<span v-if="range">
<template v-if="range.min != range.max">
@{{ range.min | price }} - @{{ range.max | price }}
</template>
<template v-else>
@{{ range.min | price }}
</template>
</span>
</div>
</price>
5 changes: 1 addition & 4 deletions resources/views/listing/partials/item/addtocart.blade.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<add-to-cart v-bind:product="item" v-cloak>
<div class="px-2 pb-2" slot-scope="{ options, error, add, disabledOptions, simpleProduct, getOptions, adding, added }">
<div class="flex items-center space-x-2 mb-2">
<div class="font-semibold">@{{ (simpleProduct.special_price || simpleProduct.price) | price }}</div>
<div class="line-through text-sm" v-if="simpleProduct.special_price">@{{ simpleProduct.price | price }}</div>
</div>
<x-rapidez::price/>

<p v-if="!item.in_stock" class="text-red-600 text-xs">@lang('Sorry! This product is currently out of stock.')</p>
<div v-else>
Expand Down
23 changes: 12 additions & 11 deletions resources/views/product/partials/addtocart.blade.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<add-to-cart :default-qty="{{ $product->min_sale_qty > $product->qty_increments ? $product->min_sale_qty : $product->qty_increments }}">
<form slot-scope="{ _renderProxy: addToCartSlotProps, options, customOptions, error, add, disabledOptions, simpleProduct, adding, added, price, specialPrice, setCustomOptionFile }" v-on:submit.prevent="add">
<form slot-scope="{ _renderProxy: addToCartSlotProps, options, customOptions, error, add, disabledOptions, simpleProduct, adding, added, setCustomOptionFile }" v-on:submit.prevent="add">
<h1 class="mb-3 text-3xl font-bold" itemprop="name">{{ $product->name }}</h1>
@if (!$product->in_stock)
<p class="text-red-600">@lang('Sorry! This product is currently out of stock.')</p>
Expand All @@ -18,15 +18,16 @@
</div>

@include('rapidez::product.partials.options')
<div class="mt-5 flex flex-wrap items-center gap-3" v-blur>
<div>
<div class="text-2xl font-bold text-neutral" v-text="$options.filters.price(specialPrice || price)">
{{ price($product->special_price ?: $product->price) }}
</div>
<div class="text-lg text-neutral line-through" v-if="specialPrice" v-text="$options.filters.price(price)">
<div class="mt-5 flex flex-wrap items-center gap-3">
<x-rapidez::price
class="text-2xl font-bold text-neutral flex-col items-start gap-0"
v-bind:options="{ product_options: customOptions }"
>
{{ price($product->special_price ?: $product->price) }}
<x-slot:special class="text-lg">
{{ $product->special_price ? price($product->price) : '' }}
</div>
</div>
</x-slot:special>
</x-rapidez::price>
<x-rapidez::select
class="w-auto"
name="qty"
Expand All @@ -41,8 +42,8 @@ class="w-auto"
</x-rapidez::select>
<x-rapidez::button type="submit" class="flex items-center" dusk="add-to-cart">
<x-heroicon-o-shopping-cart class="mr-2 h-5 w-5" v-if="!adding && !added" />
<x-heroicon-o-arrow-path class="mr-2 h-5 w-5 animate-spin" v-if="adding" />
<x-heroicon-o-check class="mr-2 h-5 w-5" v-if="added" />
<x-heroicon-o-arrow-path class="mr-2 h-5 w-5 animate-spin" v-if="adding" v-cloak />
<x-heroicon-o-check class="mr-2 h-5 w-5" v-if="added" v-cloak />
<span v-if="!adding && !added">@lang('Add to cart')</span>
<span v-if="adding" v-cloak>@lang('Adding')...</span>
<span v-if="added" v-cloak>@lang('Added')</span>
Expand Down
5 changes: 1 addition & 4 deletions resources/views/product/partials/grouped.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
<div class="contents" slot-scope="{ _renderProxy: addToCartSlotProps, add, simpleProduct, adding, added }">
<div>
@{{ simpleProduct.name }}
<div class="flex items-center space-x-3 font-bold">
<div>@{{ (simpleProduct.special_price || simpleProduct.price) | price }}</div>
<div class="line-through" v-if="simpleProduct.special_price">@{{ simpleProduct.price | price }}</div>
</div>
<x-rapidez::price/>
</div>

<p class="col-span-2 self-center text-red-600" v-if="!simpleProduct.in_stock">
Expand Down
3 changes: 3 additions & 0 deletions src/Facades/Rapidez.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
* @method static array getStores($storeId = null)
* @method static array getStore($storeId)
* @method static void setStore($store)
* @method static array getTaxTable()
* @method static array getTaxGroups()
* @method static array getTaxRates($tax_class_id)
*
* @see \Rapidez\Core\Rapidez
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Controllers/ProductController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function show(int $productId)
->with('options')
->findOrFail($productId);

$attributes = ['entity_id', 'name', 'sku', 'super_attributes', 'children', 'grouped', 'options', 'price', 'special_price', 'images', 'url', 'min_sale_qty'];
$attributes = ['entity_id', 'name', 'sku', 'super_attributes', 'children', 'grouped', 'options', 'price', 'special_price', 'tax_class_id', 'tax_rates', 'images', 'url', 'min_sale_qty'];
$attributes = Eventy::filter('productpage.frontend.attributes', $attributes);

foreach ($product->super_attributes ?: [] as $superAttribute) {
Expand Down
Loading