{#
* Plugin Name : ProductOption
* Modified for Price Difference Display
*
* Copyright (C) BraTech Co., Ltd. All Rights Reserved.
* http://www.bratech.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
#}
{% block css %}
<style type="text/css">
.datepicker-days th.dow:first-child,
.datepicker-days td:first-child {
color: #f00;
}
.datepicker-days th.dow:last-child,
.datepicker-days td:last-child {
color: #00f;
}
/* 差額表示用のスタイル */
.option-price-diff {
font-weight: normal;
font-size: 0.9em;
margin-left: 0.5em;
}
.option-price-diff.positive {
color: #d9534f;
}
.option-price-diff.negative {
color: #5cb85c;
}
.option-price-diff.zero {
color: #999;
}
</style>
{% endblock %}
{% block javascript %}
<script src="{{ asset('../../plugin/ProductOption42/assets/js/jquery.plainmodal.min.js') }}"></script>
<script>
$(function() {
//デフォルトのデイトタイムピッカーが存在しない場合
if ($('[type="date"]').prop('type') !== 'date') {
$.getScript("{{ asset('assets/js/vendor/moment.min.js', 'admin') }}").done(function() {
$.getScript("{{ asset('assets/js/vendor/moment-with-locales.min.js', 'admin') }}").done(function() {
$.getScript("{{ asset('assets/js/vendor/tempusdominus-bootstrap-4.min.js', 'admin') }}").done(function() {
$('input[type=date]').datetimepicker({
locale: '{{ eccube_config.locale }}',
format: 'YYYY-MM-DD',
useCurrent: false,
buttons: {
showToday: true,
showClose: true
}
});
});
});
});
}
});
var product_id;
$(function() {
{% for ProductOption in ProductOptions %}
{% set Option = ProductOption.Option %}
{% set Product = ProductOption.Product %}
{% if Option.description_flg == constant('Plugin\\ProductOption42\\Entity\\OptionCategory::ON') %}
modal{{ Product.id }}_{{ Option.id }} = $('#option_description_{{ Product.id }}_{{ Option.id }}').plainModal();
$('#option_description_link_{{ Product.id }}_{{ Option.id }}').click(function() {
modal{{ Product.id }}_{{ Option.id }}.plainModal('open');
product_id = $(this).attr('data');
return false;
});
{% endif %}
{% endfor %}
});
$(function() {
$("[id^=desc_btn_]").click(function(){
var form_id;
var id = $(this).attr('id').replace(/^desc_btn_/ig, '');
var ids = id.split('_');
if (eccube.hasOwnProperty('productsClassCategories')) {
form_id = product_id;
}else{
form_id = 1;
}
var $form = $("form[name=form"+form_id+"]");
func_submit($form,ids[0],ids[1]);
onOptionChange($form)
});
$("[name^=productoption]").change(function(){
$form = $(this).parents('form');
onOptionChange($form);
});
// 初回ロード時に差額表示を追加
updatePriceDifferenceDisplay();
});
function func_submit($form,optionId, setValue) {
var $sele_option = $form.find("[name=productoption" + optionId + "]");
if($sele_option && $sele_option.length){
var kind = $sele_option.attr("type");
if(kind == 'radio'){
$sele_option.val([setValue]);
}else{
$sele_option.val(setValue);
}
}else{
var $sele_option = $form.find('[name="productoption' + optionId + '[]"]');
if($sele_option && $sele_option.length){
$sele_option.each(function(){
if($(this).val() == setValue){
$(this).prop('checked',true);
}
});
}
}
$('#option_description_' + product_id + '_' + optionId).plainModal('close');
}
var optionPrice = {{ optionPrices|json_encode|raw }};
var optionPoint = {{ optionPoints|json_encode|raw }};
var optionMultiple = {{ optionMultiples|json_encode|raw }};
var optionBasePrice = {{ optionBasePrices|json_encode|raw }}; // 追加: 基準価格
var optionBaseCategoryId = {{ optionBaseCategoryIds|json_encode|raw }}; // 追加: 基準カテゴリID
var taxRules = {{ taxRules|json_encode|raw }};
var default_class_id = {{ default_class_id|json_encode|raw }};
/**
* 差額表示を更新する関数
*/
function updatePriceDifferenceDisplay() {
$('form').each(function(){
var $form = $(this);
var product_id = $form.find('input[name="product_id"]').val();
if(!product_id) return;
$form.find("[id^=productoption]").each(function(){
var id = $(this).prop("id");
var name = $(this).attr("name");
if(!name){
name = $(this).find("input").prop("id");
}
if(!name) return;
var option_id = name.replace(/productoption/ig,'');
option_id = option_id.replace(/_\d+/ig,'');
option_id = option_id.replace(/\[\]/ig,'');
// 基準価格がない場合はスキップ
if(!optionBasePrice[product_id] || !optionBasePrice[product_id][option_id]){
return;
}
var basePrice = optionBasePrice[product_id][option_id];
var kind = $(this).prop('tagName');
if(kind === 'SELECT'){
// プルダウンの場合
$(this).find('option').each(function(){
var categoryId = $(this).val();
if(categoryId && optionPrice[product_id][option_id][categoryId] !== undefined){
var price = optionPrice[product_id][option_id][categoryId];
var diff = price - basePrice;
// 基準カテゴリかどうかを判定
var isBasePrice = false;
if (optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]) {
isBasePrice = (categoryId == optionBaseCategoryId[product_id][option_id]);
} else {
// 基準カテゴリIDがない場合は差額0で判定
isBasePrice = (diff === 0 && price === basePrice);
}
var diffText = formatPriceDifference(diff, isBasePrice);
// 元のテキストから価格表示部分を削除(¥や¥を含む括弧を削除)
var originalText = $(this).text()
.replace(/[((]\s*[¥¥]\s*[\d,]+\s*[))]/g, '') // (¥ 1,000) を削除
.replace(/\s*[+\-±][\d,]+円\s*$/g, '') // 既存の差額表示を削除
.trim();
// 差額表示がある場合のみ追加
if(diffText) {
$(this).text(originalText + ' ' + diffText);
} else {
$(this).text(originalText);
}
}
});
} else if($(this).find('input[type="radio"]').length > 0){
// ラジオボタンの場合
$(this).find('input[type="radio"]').each(function(){
var categoryId = $(this).val();
if(categoryId && optionPrice[product_id][option_id][categoryId] !== undefined){
var price = optionPrice[product_id][option_id][categoryId];
var diff = price - basePrice;
// 基準カテゴリかどうかを判定
var isBasePrice = false;
if (optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]) {
isBasePrice = (categoryId == optionBaseCategoryId[product_id][option_id]);
} else {
isBasePrice = (diff === 0 && price === basePrice);
}
var diffText = formatPriceDifference(diff, isBasePrice);
var $label = $(this).next('label');
if($label.length){
// 元のテキストから価格表示部分を削除
var originalText = $label.text()
.replace(/[((]\s*[¥¥]\s*[\d,]+\s*[))]/g, '')
.replace(/\s*[+\-±][\d,]+円\s*$/g, '')
.trim();
// 差額表示がある場合のみ追加
if(diffText) {
$label.html(originalText + ' <span class="option-price-diff ' + getPriceClass(diff) + '">' + diffText + '</span>');
} else {
$label.html(originalText);
}
}
}
});
} else if($(this).find('input[type="checkbox"]').length > 0){
// チェックボックスの場合
$(this).find('input[type="checkbox"]').each(function(){
var categoryId = $(this).val();
if(categoryId && optionPrice[product_id][option_id][categoryId] !== undefined){
var price = optionPrice[product_id][option_id][categoryId];
var diff = price - basePrice;
// 基準カテゴリかどうかを判定
var isBasePrice = false;
if (optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]) {
isBasePrice = (categoryId == optionBaseCategoryId[product_id][option_id]);
} else {
isBasePrice = (diff === 0 && price === basePrice);
}
var diffText = formatPriceDifference(diff, isBasePrice);
var $label = $(this).next('label');
if($label.length){
// 元のテキストから価格表示部分を削除
var originalText = $label.text()
.replace(/[((]\s*[¥¥]\s*[\d,]+\s*[))]/g, '')
.replace(/\s*[+\-±][\d,]+円\s*$/g, '')
.trim();
// 差額表示がある場合のみ追加
if(diffText) {
$label.html(originalText + ' <span class="option-price-diff ' + getPriceClass(diff) + '">' + diffText + '</span>');
} else {
$label.html(originalText);
}
}
}
});
}
});
});
}
/**
* 差額を表示用にフォーマット
* @param {number} diff - 価格差額
* @param {boolean} isBasePrice - 基準価格かどうか
*/
function formatPriceDifference(diff, isBasePrice) {
// 基準価格(init_flg=1)の場合は空文字(表示なし)
if(isBasePrice) {
return '';
}
// 差額が0円の場合
if(diff === 0) {
return '±0円';
} else if(diff > 0) {
return '+' + Number(diff).toLocaleString() + '円';
} else {
return Number(diff).toLocaleString() + '円';
}
}
/**
* 差額に応じたCSSクラスを返す
*/
function getPriceClass(diff) {
if(diff === 0) return 'zero';
if(diff > 0) return 'positive';
return 'negative';
}
function onOptionChange($form){
if(!$form.length){
return;
}
var optionPriceTotal = 0;
var optionPointTotal = 0;
var tax_rate = null;
var tax_rule = null;
var product_id = $form.find('input[name="product_id"]').val();
var $sele1 = $form.find('select[name=classcategory_id1]');
var $sele2 = $form.find('select[name=classcategory_id2]');
var classcat_id1 = $sele1.val() ? $sele1.val() : '__unselected';
var classcat_id2 = $sele2.val() ? $sele2.val() : '';
if (eccube.hasOwnProperty('productsClassCategories')) {
// 商品一覧時
classcat2 = eccube.productsClassCategories[product_id][classcat_id1]['#' + classcat_id2];
} else {
// 詳細表示時
classcat2 = eccube.classCategories[classcat_id1]['#' + classcat_id2];
}
$form.find("[id^=productoption]").each(function(){
var id = $(this).prop("id");
var name = $(this).attr("name");
var option_id = null;
if(name === undefined){
name = $(this).find("input").prop("id");
}
if(name === undefined)return;
option_id = name.replace(/productoption/ig,'');
option_id = option_id.replace(/_\d+/ig,'');
option_id = option_id.replace(/\[\]/ig,'');
if(id.match(/^productoption\d+$/)){
var kind = $(this).prop('tagName');
var type = $(this).attr('type');
var value = null;
var multi = 1;
switch(kind){
case 'SELECT':
value = $(this).val();
break;
case 'TEXTAREA':
case 'INPUT':
var text = $(this).val();
if(text.length > 0){
value = 0;
}
if(type == 'number'){
if(optionMultiple[product_id][option_id]){
multi = $(this).val();
if(multi.length == 0)multi = 0;
}
}
break;
default:
if($(this).find('input[type="checkbox"]').length > 0){
value = [];
$(this).find('input[type="checkbox"]:checked').each(function(){
value.push($(this).val());
});
}else{
value = $form.find("input[name='productoption" + option_id + "']:checked").val();
}
break;
}
if($.isNumeric(value) || Array.isArray(value)){
// ========== 基準価格を使った差額計算 ==========
var basePrice = 0;
if(optionBasePrice[product_id] && optionBasePrice[product_id][option_id]){
basePrice = parseFloat(optionBasePrice[product_id][option_id]);
}
if(Array.isArray(value)){
for(var key in value){
var categoryPrice = parseFloat(optionPrice[product_id][option_id][value[key]]);
// 基準カテゴリかどうかを判定
var isBaseCategory = false;
if(optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]){
isBaseCategory = (value[key] == optionBaseCategoryId[product_id][option_id]);
}
// 基準カテゴリの場合は加算しない、それ以外は差額を加算
if(!isBaseCategory){
optionPriceTotal += (categoryPrice - basePrice);
// ポイントは差額分のみ
var pointDiff = parseFloat(optionPoint[product_id][option_id][value[key]]) - Math.round(basePrice * (parseFloat(optionPoint[product_id][option_id][value[key]]) / categoryPrice));
optionPointTotal += pointDiff;
}
}
}else{
var categoryPrice = parseFloat(optionPrice[product_id][option_id][value]*multi);
// 基準カテゴリかどうかを判定
var isBaseCategory = false;
if(optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]){
isBaseCategory = (value == optionBaseCategoryId[product_id][option_id]);
}
// 基準カテゴリの場合は加算しない、それ以外は差額を加算
if(!isBaseCategory){
optionPriceTotal += (categoryPrice - basePrice * multi);
// ポイントは差額分のみ
var pointDiff = parseFloat(optionPoint[product_id][option_id][value]*multi) - Math.round(basePrice * multi * (parseFloat(optionPoint[product_id][option_id][value]) / parseFloat(optionPrice[product_id][option_id][value])));
optionPointTotal += pointDiff;
}
}
// ========== 差額計算ここまで ==========
}
}
});
if(classcat2){
var product_class_id = classcat2.product_class_id;
}else{
var product_class_id = default_class_id[product_id];
}
var tax_rate = taxRules[product_class_id]['tax_rate'];
var tax_rule = taxRules[product_class_id]['tax_rule'];
var $option_price = $form.parent().find('#option_price_default').first();
$option_price.text(number_format(optionPriceTotal));
var $option_point = $form.parent().find('#option_price_inctax_default').first();
$option_point.text(number_format(optionPriceTotal + sfTax(optionPriceTotal, tax_rate, tax_rule)));
}
function number_format(num) {
return num.toString().replace(/([0-9]+?)(?=(?:[0-9]{3})+$)/g , '$1,');
}
function sfTax(price, tax_rate, tax_rule) {
real_tax = tax_rate / 100;
ret = price * real_tax;
tax_rule = parseInt(tax_rule);
switch (tax_rule) {
// 四捨五入
case {{ constant('Eccube\\Entity\\Master\\RoundingType::ROUND') }}:
$ret = Math.round(ret);
break;
// 切り捨て
case {{ constant('Eccube\\Entity\\Master\\RoundingType::FLOOR') }}:
$ret = Math.floor(ret);
break;
// 切り上げ
case {{ constant('Eccube\\Entity\\Master\\RoundingType::CEIL') }}:
$ret = Math.ceil(ret);
break;
}
return $ret;
}
</script>
{% endblock %}