app/Plugin/ProductOption42/Resource/template/default/Product/option_js.twig line 1

Open in your IDE?
  1. {#
  2. * Plugin Name : ProductOption
  3. * Modified for Price Difference Display
  4. *
  5. * Copyright (C) BraTech Co., Ltd. All Rights Reserved.
  6. * http://www.bratech.co.jp/
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. #}
  11. {% block css %}
  12. <style type="text/css">
  13.     .datepicker-days th.dow:first-child,
  14.     .datepicker-days td:first-child {
  15.         color: #f00;
  16.     }
  17.     .datepicker-days th.dow:last-child,
  18.     .datepicker-days td:last-child {
  19.         color: #00f;
  20.     }
  21.     
  22.     /* 差額表示用のスタイル */
  23.     .option-price-diff {
  24.         font-weight: normal;
  25.         font-size: 0.9em;
  26.         margin-left: 0.5em;
  27.     }
  28.     .option-price-diff.positive {
  29.         color: #d9534f;
  30.     }
  31.     .option-price-diff.negative {
  32.         color: #5cb85c;
  33.     }
  34.     .option-price-diff.zero {
  35.         color: #999;
  36.     }
  37. </style>
  38. {% endblock %}
  39. {% block javascript %}
  40. <script src="{{ asset('../../plugin/ProductOption42/assets/js/jquery.plainmodal.min.js') }}"></script>
  41. <script>
  42. $(function() {
  43.     //デフォルトのデイトタイムピッカーが存在しない場合
  44.     if ($('[type="date"]').prop('type') !== 'date') {
  45.         $.getScript("{{ asset('assets/js/vendor/moment.min.js', 'admin') }}").done(function() {
  46.             $.getScript("{{ asset('assets/js/vendor/moment-with-locales.min.js', 'admin') }}").done(function() {
  47.                 $.getScript("{{ asset('assets/js/vendor/tempusdominus-bootstrap-4.min.js', 'admin') }}").done(function() {
  48.                     $('input[type=date]').datetimepicker({
  49.                         locale: '{{ eccube_config.locale }}',
  50.                         format: 'YYYY-MM-DD',
  51.                         useCurrent: false,
  52.                         buttons: {
  53.                             showToday: true,
  54.                             showClose: true
  55.                         }
  56.                     });
  57.                 });
  58.             });
  59.         });
  60.     }
  61. });
  62. var product_id;
  63. $(function() {
  64.     {% for ProductOption in ProductOptions %}
  65.     {% set Option = ProductOption.Option %}
  66.     {% set Product = ProductOption.Product %}
  67.         {% if Option.description_flg == constant('Plugin\\ProductOption42\\Entity\\OptionCategory::ON') %}
  68.             modal{{ Product.id }}_{{ Option.id }} = $('#option_description_{{ Product.id }}_{{ Option.id }}').plainModal();
  69.             $('#option_description_link_{{ Product.id }}_{{ Option.id }}').click(function() {
  70.                 modal{{ Product.id }}_{{ Option.id }}.plainModal('open');
  71.                 product_id = $(this).attr('data');
  72.                 return false;
  73.             });
  74.         {% endif %}
  75.     {% endfor %}
  76. });
  77. $(function() {
  78.     $("[id^=desc_btn_]").click(function(){
  79.         var form_id;
  80.         var id = $(this).attr('id').replace(/^desc_btn_/ig, '');
  81.         var ids = id.split('_');
  82.         if (eccube.hasOwnProperty('productsClassCategories')) {
  83.             form_id = product_id;
  84.         }else{
  85.             form_id = 1;
  86.         }
  87.         var $form = $("form[name=form"+form_id+"]");
  88.         func_submit($form,ids[0],ids[1]);
  89.         onOptionChange($form)
  90.     });
  91.     $("[name^=productoption]").change(function(){
  92.         $form = $(this).parents('form');
  93.         onOptionChange($form);
  94.     });
  95.     
  96.     // 初回ロード時に差額表示を追加
  97.     updatePriceDifferenceDisplay();
  98. });
  99. function func_submit($form,optionId, setValue) {
  100.     var $sele_option = $form.find("[name=productoption" + optionId + "]");
  101.     if($sele_option && $sele_option.length){
  102.         var kind = $sele_option.attr("type");
  103.         if(kind == 'radio'){
  104.             $sele_option.val([setValue]);
  105.         }else{
  106.             $sele_option.val(setValue);
  107.         }
  108.     }else{
  109.         var $sele_option = $form.find('[name="productoption' + optionId + '[]"]');
  110.         if($sele_option && $sele_option.length){
  111.             $sele_option.each(function(){
  112.                 if($(this).val() == setValue){
  113.                     $(this).prop('checked',true);
  114.                 }
  115.             });
  116.         }
  117.     }
  118.     $('#option_description_' + product_id + '_' + optionId).plainModal('close');
  119. }
  120. var optionPrice = {{ optionPrices|json_encode|raw }};
  121. var optionPoint = {{ optionPoints|json_encode|raw }};
  122. var optionMultiple = {{ optionMultiples|json_encode|raw }};
  123. var optionBasePrice = {{ optionBasePrices|json_encode|raw }};  // 追加: 基準価格
  124. var optionBaseCategoryId = {{ optionBaseCategoryIds|json_encode|raw }};  // 追加: 基準カテゴリID
  125. var taxRules = {{ taxRules|json_encode|raw }};
  126. var default_class_id = {{ default_class_id|json_encode|raw }};
  127. /**
  128.  * 差額表示を更新する関数
  129.  */
  130. function updatePriceDifferenceDisplay() {
  131.     $('form').each(function(){
  132.         var $form = $(this);
  133.         var product_id = $form.find('input[name="product_id"]').val();
  134.         if(!product_id) return;
  135.         
  136.         $form.find("[id^=productoption]").each(function(){
  137.             var id = $(this).prop("id");
  138.             var name = $(this).attr("name");
  139.             if(!name){
  140.                 name = $(this).find("input").prop("id");
  141.             }
  142.             if(!name) return;
  143.             
  144.             var option_id = name.replace(/productoption/ig,'');
  145.             option_id = option_id.replace(/_\d+/ig,'');
  146.             option_id = option_id.replace(/\[\]/ig,'');
  147.             
  148.             // 基準価格がない場合はスキップ
  149.             if(!optionBasePrice[product_id] || !optionBasePrice[product_id][option_id]){
  150.                 return;
  151.             }
  152.             
  153.             var basePrice = optionBasePrice[product_id][option_id];
  154.             var kind = $(this).prop('tagName');
  155.             
  156.             if(kind === 'SELECT'){
  157.                 // プルダウンの場合
  158.                 $(this).find('option').each(function(){
  159.                     var categoryId = $(this).val();
  160.                     if(categoryId && optionPrice[product_id][option_id][categoryId] !== undefined){
  161.                         var price = optionPrice[product_id][option_id][categoryId];
  162.                         var diff = price - basePrice;
  163.                         
  164.                         // 基準カテゴリかどうかを判定
  165.                         var isBasePrice = false;
  166.                         if (optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]) {
  167.                             isBasePrice = (categoryId == optionBaseCategoryId[product_id][option_id]);
  168.                         } else {
  169.                             // 基準カテゴリIDがない場合は差額0で判定
  170.                             isBasePrice = (diff === 0 && price === basePrice);
  171.                         }
  172.                         
  173.                         var diffText = formatPriceDifference(diff, isBasePrice);
  174.                         
  175.                         // 元のテキストから価格表示部分を削除(¥や¥を含む括弧を削除)
  176.                         var originalText = $(this).text()
  177.                             .replace(/[((]\s*[¥¥]\s*[\d,]+\s*[))]/g, '')  // (¥ 1,000) を削除
  178.                             .replace(/\s*[+\-±][\d,]+円\s*$/g, '')  // 既存の差額表示を削除
  179.                             .trim();
  180.                         
  181.                         // 差額表示がある場合のみ追加
  182.                         if(diffText) {
  183.                             $(this).text(originalText + ' ' + diffText);
  184.                         } else {
  185.                             $(this).text(originalText);
  186.                         }
  187.                     }
  188.                 });
  189.             } else if($(this).find('input[type="radio"]').length > 0){
  190.                 // ラジオボタンの場合
  191.                 $(this).find('input[type="radio"]').each(function(){
  192.                     var categoryId = $(this).val();
  193.                     if(categoryId && optionPrice[product_id][option_id][categoryId] !== undefined){
  194.                         var price = optionPrice[product_id][option_id][categoryId];
  195.                         var diff = price - basePrice;
  196.                         
  197.                         // 基準カテゴリかどうかを判定
  198.                         var isBasePrice = false;
  199.                         if (optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]) {
  200.                             isBasePrice = (categoryId == optionBaseCategoryId[product_id][option_id]);
  201.                         } else {
  202.                             isBasePrice = (diff === 0 && price === basePrice);
  203.                         }
  204.                         
  205.                         var diffText = formatPriceDifference(diff, isBasePrice);
  206.                         var $label = $(this).next('label');
  207.                         if($label.length){
  208.                             // 元のテキストから価格表示部分を削除
  209.                             var originalText = $label.text()
  210.                                 .replace(/[((]\s*[¥¥]\s*[\d,]+\s*[))]/g, '')
  211.                                 .replace(/\s*[+\-±][\d,]+円\s*$/g, '')
  212.                                 .trim();
  213.                             
  214.                             // 差額表示がある場合のみ追加
  215.                             if(diffText) {
  216.                                 $label.html(originalText + ' <span class="option-price-diff ' + getPriceClass(diff) + '">' + diffText + '</span>');
  217.                             } else {
  218.                                 $label.html(originalText);
  219.                             }
  220.                         }
  221.                     }
  222.                 });
  223.             } else if($(this).find('input[type="checkbox"]').length > 0){
  224.                 // チェックボックスの場合
  225.                 $(this).find('input[type="checkbox"]').each(function(){
  226.                     var categoryId = $(this).val();
  227.                     if(categoryId && optionPrice[product_id][option_id][categoryId] !== undefined){
  228.                         var price = optionPrice[product_id][option_id][categoryId];
  229.                         var diff = price - basePrice;
  230.                         
  231.                         // 基準カテゴリかどうかを判定
  232.                         var isBasePrice = false;
  233.                         if (optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]) {
  234.                             isBasePrice = (categoryId == optionBaseCategoryId[product_id][option_id]);
  235.                         } else {
  236.                             isBasePrice = (diff === 0 && price === basePrice);
  237.                         }
  238.                         
  239.                         var diffText = formatPriceDifference(diff, isBasePrice);
  240.                         var $label = $(this).next('label');
  241.                         if($label.length){
  242.                             // 元のテキストから価格表示部分を削除
  243.                             var originalText = $label.text()
  244.                                 .replace(/[((]\s*[¥¥]\s*[\d,]+\s*[))]/g, '')
  245.                                 .replace(/\s*[+\-±][\d,]+円\s*$/g, '')
  246.                                 .trim();
  247.                             
  248.                             // 差額表示がある場合のみ追加
  249.                             if(diffText) {
  250.                                 $label.html(originalText + ' <span class="option-price-diff ' + getPriceClass(diff) + '">' + diffText + '</span>');
  251.                             } else {
  252.                                 $label.html(originalText);
  253.                             }
  254.                         }
  255.                     }
  256.                 });
  257.             }
  258.         });
  259.     });
  260. }
  261. /**
  262.  * 差額を表示用にフォーマット
  263.  * @param {number} diff - 価格差額
  264.  * @param {boolean} isBasePrice - 基準価格かどうか
  265.  */
  266. function formatPriceDifference(diff, isBasePrice) {
  267.     // 基準価格(init_flg=1)の場合は空文字(表示なし)
  268.     if(isBasePrice) {
  269.         return '';
  270.     }
  271.     
  272.     // 差額が0円の場合
  273.     if(diff === 0) {
  274.         return '±0円';
  275.     } else if(diff > 0) {
  276.         return '+' + Number(diff).toLocaleString() + '円';
  277.     } else {
  278.         return Number(diff).toLocaleString() + '円';
  279.     }
  280. }
  281. /**
  282.  * 差額に応じたCSSクラスを返す
  283.  */
  284. function getPriceClass(diff) {
  285.     if(diff === 0) return 'zero';
  286.     if(diff > 0) return 'positive';
  287.     return 'negative';
  288. }
  289. function onOptionChange($form){
  290.     if(!$form.length){
  291.         return;
  292.     }
  293.     var optionPriceTotal = 0;
  294.     var optionPointTotal = 0;
  295.     var tax_rate = null;
  296.     var tax_rule = null;
  297.     var product_id = $form.find('input[name="product_id"]').val();
  298.     var $sele1 = $form.find('select[name=classcategory_id1]');
  299.     var $sele2 = $form.find('select[name=classcategory_id2]');
  300.     var classcat_id1 = $sele1.val() ? $sele1.val() : '__unselected';
  301.     var classcat_id2 = $sele2.val() ? $sele2.val() : '';
  302.     if (eccube.hasOwnProperty('productsClassCategories')) {
  303.         // 商品一覧時
  304.         classcat2 = eccube.productsClassCategories[product_id][classcat_id1]['#' + classcat_id2];
  305.     } else {
  306.         // 詳細表示時
  307.         classcat2 = eccube.classCategories[classcat_id1]['#' + classcat_id2];
  308.     }
  309.     $form.find("[id^=productoption]").each(function(){
  310.         var id = $(this).prop("id");
  311.         var name = $(this).attr("name");
  312.         var option_id = null;
  313.         if(name === undefined){
  314.             name = $(this).find("input").prop("id");
  315.         }
  316.         if(name === undefined)return;
  317.         option_id = name.replace(/productoption/ig,'');
  318.         option_id = option_id.replace(/_\d+/ig,'');
  319.         option_id = option_id.replace(/\[\]/ig,'');
  320.         if(id.match(/^productoption\d+$/)){
  321.             var kind = $(this).prop('tagName');
  322.             var type = $(this).attr('type');
  323.             var value = null;
  324.             var multi = 1;
  325.             switch(kind){
  326.                 case 'SELECT':
  327.                     value = $(this).val();
  328.                     break;
  329.                 case 'TEXTAREA':
  330.                 case 'INPUT':
  331.                     var text = $(this).val();
  332.                     if(text.length > 0){
  333.                         value = 0;
  334.                     }
  335.                     if(type == 'number'){
  336.                         if(optionMultiple[product_id][option_id]){
  337.                             multi = $(this).val();
  338.                             if(multi.length == 0)multi = 0;
  339.                         }
  340.                     }
  341.                     break;
  342.                 default:
  343.                     if($(this).find('input[type="checkbox"]').length > 0){
  344.                         value = [];
  345.                         $(this).find('input[type="checkbox"]:checked').each(function(){
  346.                             value.push($(this).val());
  347.                         });
  348.                     }else{
  349.                         value = $form.find("input[name='productoption" + option_id + "']:checked").val();
  350.                     }
  351.                     break;
  352.             }
  353.             if($.isNumeric(value) || Array.isArray(value)){
  354.                 // ========== 基準価格を使った差額計算 ==========
  355.                 var basePrice = 0;
  356.                 if(optionBasePrice[product_id] && optionBasePrice[product_id][option_id]){
  357.                     basePrice = parseFloat(optionBasePrice[product_id][option_id]);
  358.                 }
  359.                 
  360.                 if(Array.isArray(value)){
  361.                     for(var key in value){
  362.                         var categoryPrice = parseFloat(optionPrice[product_id][option_id][value[key]]);
  363.                         
  364.                         // 基準カテゴリかどうかを判定
  365.                         var isBaseCategory = false;
  366.                         if(optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]){
  367.                             isBaseCategory = (value[key] == optionBaseCategoryId[product_id][option_id]);
  368.                         }
  369.                         
  370.                         // 基準カテゴリの場合は加算しない、それ以外は差額を加算
  371.                         if(!isBaseCategory){
  372.                             optionPriceTotal += (categoryPrice - basePrice);
  373.                             // ポイントは差額分のみ
  374.                             var pointDiff = parseFloat(optionPoint[product_id][option_id][value[key]]) - Math.round(basePrice * (parseFloat(optionPoint[product_id][option_id][value[key]]) / categoryPrice));
  375.                             optionPointTotal += pointDiff;
  376.                         }
  377.                     }
  378.                 }else{
  379.                     var categoryPrice = parseFloat(optionPrice[product_id][option_id][value]*multi);
  380.                     
  381.                     // 基準カテゴリかどうかを判定
  382.                     var isBaseCategory = false;
  383.                     if(optionBaseCategoryId[product_id] && optionBaseCategoryId[product_id][option_id]){
  384.                         isBaseCategory = (value == optionBaseCategoryId[product_id][option_id]);
  385.                     }
  386.                     
  387.                     // 基準カテゴリの場合は加算しない、それ以外は差額を加算
  388.                     if(!isBaseCategory){
  389.                         optionPriceTotal += (categoryPrice - basePrice * multi);
  390.                         // ポイントは差額分のみ
  391.                         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])));
  392.                         optionPointTotal += pointDiff;
  393.                     }
  394.                 }
  395.                 // ========== 差額計算ここまで ==========
  396.             }
  397.         }
  398.     });
  399.     if(classcat2){
  400.         var product_class_id = classcat2.product_class_id;
  401.     }else{
  402.         var product_class_id = default_class_id[product_id];
  403.     }
  404.     var tax_rate = taxRules[product_class_id]['tax_rate'];
  405.     var tax_rule = taxRules[product_class_id]['tax_rule'];
  406.     
  407.     var $option_price = $form.parent().find('#option_price_default').first();
  408.     $option_price.text(number_format(optionPriceTotal));
  409.     var $option_point = $form.parent().find('#option_price_inctax_default').first();
  410.     $option_point.text(number_format(optionPriceTotal + sfTax(optionPriceTotal, tax_rate, tax_rule)));
  411. }
  412. function number_format(num) {
  413.     return num.toString().replace(/([0-9]+?)(?=(?:[0-9]{3})+$)/g , '$1,');
  414. }
  415. function sfTax(price, tax_rate, tax_rule) {
  416.     real_tax = tax_rate / 100;
  417.     ret = price * real_tax;
  418.     tax_rule = parseInt(tax_rule);
  419.     switch (tax_rule) {
  420.         // 四捨五入
  421.         case {{ constant('Eccube\\Entity\\Master\\RoundingType::ROUND') }}:
  422.             $ret = Math.round(ret);
  423.             break;
  424.         // 切り捨て
  425.         case {{ constant('Eccube\\Entity\\Master\\RoundingType::FLOOR') }}:
  426.             $ret = Math.floor(ret);
  427.             break;
  428.         // 切り上げ
  429.         case {{ constant('Eccube\\Entity\\Master\\RoundingType::CEIL') }}:
  430.             $ret = Math.ceil(ret);
  431.             break;
  432.     }
  433.     return $ret;
  434. }
  435. </script>
  436. {% endblock %}