From 45c3c24a1f37a97b8fe2f73af72aad439b686381 Mon Sep 17 00:00:00 2001 From: Werner Hahn Date: Fri, 22 Sep 2017 02:33:23 +0200 Subject: [PATCH] WebshopApi: ShopPart Controller --- SL/Controller/ShopPart.pm | 493 ++++++++++++++++++ image/gruener_punkt.gif | Bin 0 -> 89 bytes image/roter_punkt.gif | Bin 0 -> 125 bytes js/kivi.ShopPart.js | 151 ++++++ templates/webpages/shop_part/_filter.html | 28 + .../webpages/shop_part/_list_articles.html | 136 +++++ .../webpages/shop_part/_list_images.html | 35 ++ .../webpages/shop_part/_transfer_status.html | 52 ++ .../webpages/shop_part/_upload_status.html | 51 ++ templates/webpages/shop_part/categories.html | 55 ++ templates/webpages/shop_part/edit.html | 81 +++ 11 files changed, 1082 insertions(+) create mode 100644 SL/Controller/ShopPart.pm create mode 100644 image/gruener_punkt.gif create mode 100644 image/roter_punkt.gif create mode 100644 js/kivi.ShopPart.js create mode 100644 templates/webpages/shop_part/_filter.html create mode 100644 templates/webpages/shop_part/_list_articles.html create mode 100644 templates/webpages/shop_part/_list_images.html create mode 100644 templates/webpages/shop_part/_transfer_status.html create mode 100644 templates/webpages/shop_part/_upload_status.html create mode 100644 templates/webpages/shop_part/categories.html create mode 100644 templates/webpages/shop_part/edit.html diff --git a/SL/Controller/ShopPart.pm b/SL/Controller/ShopPart.pm new file mode 100644 index 000000000..fb2fe92fb --- /dev/null +++ b/SL/Controller/ShopPart.pm @@ -0,0 +1,493 @@ +package SL::Controller::ShopPart; + +use strict; + +use parent qw(SL::Controller::Base); + +use SL::BackgroundJob::ShopPartMassUpload; +use SL::System::TaskServer; +use Data::Dumper; +use SL::Locale::String qw(t8); +use SL::DB::ShopPart; +use SL::DB::Shop; +use SL::DB::File; +use SL::DB::ShopImage; +use SL::DB::Default; +use SL::Helper::Flash; +use SL::Controller::Helper::ParseFilter; +use MIME::Base64; + +use Rose::Object::MakeMethods::Generic +( + scalar => [ qw(price_sources) ], + 'scalar --get_set_init' => [ qw(shop_part file shops) ], +); + +__PACKAGE__->run_before('check_auth'); +__PACKAGE__->run_before('add_javascripts', only => [ qw(edit_popup list_articles) ]); +__PACKAGE__->run_before('load_pricesources', only => [ qw(create_or_edit_popup) ]); + +# +# actions +# + +sub action_create_or_edit_popup { + my ($self) = @_; + + $self->render_shop_part_edit_dialog(); +} + +sub action_update_shop { + my ($self, %params) = @_; + + my $shop_part = SL::DB::Manager::ShopPart->find_by(id => $::form->{shop_part_id}); + die unless $shop_part; + + require SL::Shop; + my $shop = SL::Shop->new( config => $shop_part->shop ); + + my $connect = $shop->check_connectivity; + if($connect->{success}){ + my $return = $shop->connector->update_part($self->shop_part, 'all'); + + # the connector deals with parsing/result verification, just needs to return success or failure + if ( $return == 1 ) { + my $now = DateTime->now; + my $attributes->{last_update} = $now; + $self->shop_part->assign_attributes(%{ $attributes }); + $self->shop_part->save; + $self->js->html('#shop_part_last_update_' . $shop_part->id, $now->to_kivitendo('precision' => 'minute')) + ->flash('info', t8("Updated part [#1] in shop [#2] at #3", $shop_part->part->displayable_name, $shop_part->shop->description, $now->to_kivitendo('precision' => 'minute') ) ) + ->render; + } else { + $self->js->flash('error', t8('The shop part wasn\'t updated.'))->render; + } + }else{ + $self->js->flash('error', t8('The shop part wasn\'t updated. #1', $connect->{data}->{version}))->render; + } + + +} + +sub action_show_files { + my ($self) = @_; + + my $images = SL::DB::Manager::ShopImage->get_all( where => [ 'files.object_id' => $::form->{id}, ], with_objects => 'file', sort_by => 'position' ); + + $self->render('shop_part/_list_images', { header => 0 }, IMAGES => $images); +} + +sub action_ajax_delete_file { + my ( $self ) = @_; + $self->file->delete; + + $self->js + ->run('kivi.ShopPart.show_images',$self->file->object_id) + ->render(); +} + +sub action_get_categories { + my ($self) = @_; + + require SL::Shop; + my $shop = SL::Shop->new( config => $self->shop_part->shop ); + + my $connect = $shop->check_connectivity; + if($connect->{success}){ + my $categories = $shop->connector->get_categories; + + $self->js + ->run( + 'kivi.ShopPart.shop_part_dialog', + t8('Shopcategories'), + $self->render('shop_part/categories', { output => 0 }, CATEGORIES => $categories ) + ) + ->reinit_widgets; + $self->js->render; + }else{ + $self->js->flash('error', t8('Can\'t connect to shop. #1', $connect->{data}->{version}))->render; + } + +} + +sub action_show_price_n_pricesource { + my ($self) = @_; + + my ( $price, $price_src_str ) = $self->get_price_n_pricesource($::form->{pricesource}); + + if( $price_src_str eq 'sellprice'){ + $price_src_str = t8('Sellprice'); + }elsif( $price_src_str eq 'listprice'){ + $price_src_str = t8('Listprice'); + }elsif( $price_src_str eq 'lastcost'){ + $price_src_str = t8('Lastcost'); + } + $self->js->html('#price_' . $self->shop_part->id, $::form->format_amount(\%::myconfig,$price,2)) + ->html('#active_price_source_' . $self->shop_part->id, $price_src_str) + ->render; +} + +sub action_show_stock { + my ($self) = @_; + my ( $stock_local, $stock_onlineshop, $active_online ); + + require SL::Shop; + my $shop = SL::Shop->new( config => $self->shop_part->shop ); + + if($self->shop_part->last_update) { + my $shop_article = $shop->connector->get_article($self->shop_part->part->partnumber); + $stock_onlineshop = $shop_article->{data}->{mainDetail}->{inStock}; + $active_online = $shop_article->{data}->{active}; + } + + $stock_local = $self->shop_part->part->onhand; + + $self->js->html('#stock_' . $self->shop_part->id, $::form->format_amount(\%::myconfig,$stock_local,0)."/".$::form->format_amount(\%::myconfig,$stock_onlineshop,0)) + ->html('#toogle_' . $self->shop_part->id,$active_online) + ->render; +} + +sub action_get_n_write_categories { + my ($self) = @_; + + my @shop_parts = @{ $::form->{shop_parts_ids} || [] }; + foreach my $part(@shop_parts){ + + my $shop_part = SL::DB::Manager::ShopPart->get_all( where => [id => $part], with_objects => ['part', 'shop'])->[0]; + require SL::DB::Shop; + my $shop = SL::Shop->new( config => $shop_part->shop ); + my $online_article = $shop->connector->get_article($shop_part->part->partnumber); + my $online_cat = $online_article->{data}->{categories}; + my @cat = (); + for(keys %$online_cat){ + my @cattmp; + push @cattmp,$online_cat->{$_}->{id}; + push @cattmp,$online_cat->{$_}->{name}; + push @cat,\@cattmp; + } + my $attributes->{shop_category} = \@cat; + my $active->{active} = $online_article->{data}->{active}; + $shop_part->assign_attributes(%{$attributes}, %{$active}); + $shop_part->save; + } + $self->redirect_to( action => 'list_articles' ); +} + +sub action_save_categories { + my ($self) = @_; + + my @categories = @{ $::form->{categories} || [] }; + + my @cat = (); + foreach my $cat ( @categories) { + my @cattmp; + push( @cattmp,$cat ); + push( @cattmp,$::form->{"cat_id_${cat}"} ); + push( @cat,\@cattmp ); + } + + my $categories->{shop_category} = \@cat; + + my $params = delete($::form->{shop_part}) || { }; + + $self->shop_part->assign_attributes(%{ $params }); + $self->shop_part->assign_attributes(%{ $categories }); + + $self->shop_part->save; + + flash('info', t8('The categories has been saved.')); + + $self->js->run('kivi.ShopPart.close_dialog') + ->flash('info', t8("Updated categories")) + ->render; +} + +sub action_reorder { + my ($self) = @_; + require SL::DB::ShopImage; + SL::DB::ShopImage->reorder_list(@{ $::form->{image_id} || [] }); + + $self->render(\'', { type => 'json' }); +} + +sub action_list_articles { + my ($self) = @_; + + my %filter = ($::form->{filter} ? parse_filter($::form->{filter}) : query => [ 'shop.obsolete' => 0 ]); + my $sort_by = $::form->{sort_by} ? $::form->{sort_by} : 'part.partnumber'; + $sort_by .=$::form->{sort_dir} ? ' DESC' : ' ASC'; + + my $articles = SL::DB::Manager::ShopPart->get_all( %filter ,with_objects => [ 'part','shop' ], sort_by => $sort_by ); + + foreach my $article (@{ $articles}) { + my $images = SL::DB::Manager::ShopImage->get_all_count( where => [ 'files.object_id' => $article->part->id, ], with_objects => 'file', sort_by => 'position' ); + $article->{images} = $images; + } + + $self->render('shop_part/_list_articles', title => t8('Webshops articles'), SHOP_PARTS => $articles); +} + +sub action_upload_status { + my ($self) = @_; + my $job = SL::DB::BackgroundJob->new(id => $::form->{job_id})->load; + my $html = $self->render('shop_part/_upload_status', { output => 0 }, job => $job); + + $self->js->html('#status_mass_upload', $html); + $self->js->run('kivi.ShopPart.massUploadFinished') if $job->data_as_hash->{status} == SL::BackgroundJob::ShopPartMassUpload->DONE(); + $self->js->render; +} + +sub action_mass_upload { + my ($self) = @_; + + my @shop_parts = @{ $::form->{shop_parts_ids} || [] }; + + my $job = SL::DB::BackgroundJob->new( + type => 'once', + active => 1, + package_name => 'ShopPartMassUpload', + )->set_data( + shop_part_record_ids => [ @shop_parts ], + todo => $::form->{upload_todo}, + status => SL::BackgroundJob::ShopPartMassUpload->WAITING_FOR_EXECUTION(), + conversation => [ ], + num_uploaded => 0, + )->update_next_run_at; + + SL::System::TaskServer->new->wake_up; + + my $html = $self->render('shop_part/_upload_status', { output => 0 }, job => $job); + + $self->js + ->html('#status_mass_upload', $html) + ->run('kivi.ShopPart.massUploadStarted') + ->render; +} + +sub action_update { + my ($self) = @_; + + $self->create_or_update; +} + +sub render_shop_part_edit_dialog { + my ($self) = @_; + + $self->js + ->run( + 'kivi.ShopPart.shop_part_dialog', + t8('Shop part'), + $self->render('shop_part/edit', { output => 0 }) + ) + ->reinit_widgets; + + $self->js->render; +} + +sub create_or_update { + my ($self) = @_; + + my $is_new = !$self->shop_part->id; + + my $params = delete($::form->{shop_part}) || { }; + + $self->shop_part->assign_attributes(%{ $params }); + + $self->shop_part->save; + + my ( $price, $price_src_str ) = $self->get_price_n_pricesource($self->shop_part->active_price_source); +if(!$is_new){ + flash('info', $is_new ? t8('The shop part has been created.') : t8('The shop part has been saved.')); + $self->js->html('#shop_part_description_' . $self->shop_part->id, $self->shop_part->shop_description) + ->html('#shop_part_active_' . $self->shop_part->id, $self->shop_part->active) + ->html('#price_' . $self->shop_part->id, $::form->format_amount(\%::myconfig,$price,2)) + ->html('#active_price_source_' . $self->shop_part->id, $price_src_str) + ->run('kivi.ShopPart.close_dialog') + ->flash('info', t8("Updated shop part")) + ->render; + }else{ + $self->redirect_to(controller => 'Part', action => 'edit', 'part.id' => $self->shop_part->part_id); + } +} + +# +# internal stuff +# +sub add_javascripts { + $::request->{layout}->add_javascripts(qw(kivi.ShopPart.js)); +} + +sub load_pricesources { + my ($self) = @_; + + my $pricesources; + push( @{ $pricesources } , { id => "master_data/sellprice", name => t8("Master Data")." - ".t8("Sellprice") }, + { id => "master_data/listprice", name => t8("Master Data")." - ".t8("Listprice") }, + { id => "master_data/lastcost", name => t8("Master Data")." - ".t8("Lastcost") } + ); + my $pricegroups = SL::DB::Manager::Pricegroup->get_all; + foreach my $pg ( @$pricegroups ) { + push( @{ $pricesources } , { id => "pricegroup/".$pg->id, name => t8("Pricegroup") . " - " . $pg->pricegroup} ); + }; + + $self->price_sources( $pricesources ); +} + +sub get_price_n_pricesource { + my ($self,$pricesource) = @_; + + my ( $price_src_str, $price_src_id ) = split(/\//,$pricesource); + + require SL::DB::Pricegroup; + require SL::DB::Part; + my $price; + if ($price_src_str eq "master_data") { + my $part = SL::DB::Manager::Part->find_by( id => $self->shop_part->part_id ); + $price = $part->$price_src_id; + $price_src_str = $price_src_id; + }else{ + my $part = SL::DB::Manager::Part->get_all( where => [id => $self->shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id], with_objects => ['prices'],limit => 1)->[0]; + #my $part = SL::DB::Manager::Part->find_by( id => $self->shop_part->part_id, 'prices.'.pricegroup_id => $price_src_id ); + my $pricegrp = SL::DB::Manager::Pricegroup->find_by( id => $price_src_id )->pricegroup; + $price = $part->prices->[0]->price; + $price_src_str = $pricegrp; + } + return($price,$price_src_str); +} + +sub check_auth { + $::auth->assert('shop_part_edit'); +} + +sub init_shop_part { + if ($::form->{shop_part_id}) { + SL::DB::Manager::ShopPart->find_by(id => $::form->{shop_part_id}); + } else { + SL::DB::ShopPart->new(shop_id => $::form->{shop_id}, part_id => $::form->{part_id}); + }; +} + +sub init_file { + my $file = $::form->{id} ? SL::DB::File->new(id => $::form->{id})->load : SL::DB::File->new; + return $file; +} + +sub init_shops { + SL::DB::Shop->shops_dd; +} + +1; + +__END__ + +=encoding utf-8 + + +=head1 NAME + +SL::Controller::ShopPart - Controller for managing ShopParts + +=head1 SYNOPSIS + +ShopParts are configured in a tab of the corresponding part. + +=head1 ACTIONS + +=over 4 + +=item C + +To be called from the "Update" button of the shoppart, for manually syncing/upload one part with its shop. Calls some ClientJS functions to modifiy original page. + +=item C + + + +=item C + + + +=item C + + + +=item C + + + +=item C + + + +=item C + +Can be used to sync the categories of a shoppart with the categories from online. + +=item C + +The ShopwareConnector works with the CategoryID @categories[x][0] in others/new Connectors it must be tested +Each assigned categorie is saved with id,categorie_name an multidimensional array and could be expanded with categoriepath or what is needed + +=item C + + + +=item C + + + +=item C + + + +=item C + + + +=item C + + + +=item C + +when self->shop_part is called in template, it will be an existing shop_part with id, +or a new shop_part with only part_id and shop_id set + +=item C + + +=item C + +the price sources to use for the article: sellprice, lastcost, +listprice, or one of the pricegroups. It overwrites the default pricesource from the shopconfig. +TODO: implement valid pricerules for the article + +=item C + + +=item C + + +=item C + + +=item C + + +=item C + +data for drop down filter options + +=back + +=head1 TODO + +CheckAuth +Pricesrules, pricessources aren't fully implemented yet. + +=head1 AUTHORS + +G. Richardson Einformation@kivitendo-premium.deE +W. Hahn Ewh@futureworldsearch.netE + +=cut diff --git a/image/gruener_punkt.gif b/image/gruener_punkt.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe6c55d4eafc635107291d4dc13aa37e0e2c90cf GIT binary patch literal 89 zcmZ?wbhEHbXIGlAGFSruv8Egv literal 0 HcmV?d00001 diff --git a/image/roter_punkt.gif b/image/roter_punkt.gif new file mode 100644 index 0000000000000000000000000000000000000000..2331af435106a268329d4012926a1f07b9f48d9f GIT binary patch literal 125 zcmZ?wbhEHbq_)pNeC^fMpHASI3vm`^o z-P1RKLGdRGBNqcZgAM}_fDB+@)~#4|W$%Y6j_t~YVb$U{LzpeNEI6)zZhU#v%rSal XZtnf>ijR2yeX;Z~SoGRKh`|~Fiqb7> literal 0 HcmV?d00001 diff --git a/js/kivi.ShopPart.js b/js/kivi.ShopPart.js new file mode 100644 index 000000000..64a1e46d7 --- /dev/null +++ b/js/kivi.ShopPart.js @@ -0,0 +1,151 @@ +namespace('kivi.ShopPart', function(ns) { + var $dialog; + + ns.shop_part_dialog = function(title, html) { + var id = 'jqueryui_popup_dialog'; + var dialog_params = { + id: id, + width: 800, + height: 500, + modal: true, + close: function(event, ui) { $dialog.remove(); }, + }; + + $('#' + id).remove(); + + $dialog = $('').appendTo('body'); + $dialog.attr('title', title); + $dialog.html(html); + $dialog.dialog(dialog_params); + + $('.cancel').click(ns.close_dialog); + + return true; + }; + + ns.close_dialog = function() { + $dialog.dialog("close"); + } + + ns.save_shop_part = function(shop_part_id) { + var form = $('form').serializeArray(); + form.push( { name: 'action', value: 'ShopPart/update' } + , { name: 'shop_part_id', value: shop_part_id } + ); + + $.post('controller.pl', form, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.add_shop_part = function(part_id,shop_id) { + var form = $('form').serializeArray(); + form.push( { name: 'action', value: 'ShopPart/update' } + ); + $.post('controller.pl', form, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.edit_shop_part = function(shop_part_id) { + $.post('controller.pl', { action: 'ShopPart/create_or_edit_popup', shop_part_id: shop_part_id }, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.create_shop_part = function(part_id, shop_id) { + $.post('controller.pl', { action: 'ShopPart/create_or_edit_popup', part_id: part_id, shop_id: shop_id }, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.get_all_categories = function(shop_part_id) { + $.post('controller.pl', { action: 'ShopPart/get_categories', shop_part_id: shop_part_id }, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.save_categories = function(shop_part_id, shop_id) { + var form = $('form').serializeArray(); + form.push( { name: 'action', value: 'ShopPart/save_categories' } + , { name: 'shop_id', value: shop_id } + , { name: 'shop_part_id', value: shop_part_id } + ); + + $.post('controller.pl', form, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.update_shop_part = function(shop_part_id) { + $.post('controller.pl', { action: 'ShopPart/update_shop', shop_part_id: shop_part_id }, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.update_discount_source = function(row, source, discount_str) { + $('#active_discount_source_' + row).val(source); + if (discount_str) $('#discount_' + row).val(discount_str); + $('#update_button').click(); + } + + ns.show_images = function(id) { + var url = 'controller.pl?action=ShopPart/show_files&id='+id; + $('#shop_images').load(url); + } + + ns.update_price_n_price_source = function(shop_part_id,price_source) { + $.post('controller.pl', { action: 'ShopPart/show_price_n_pricesource', shop_part_id: shop_part_id, pricesource: price_source }, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.update_stock = function(shop_part_id) { + $.post('controller.pl', { action: 'ShopPart/show_stock', shop_part_id: shop_part_id }, function(data) { + kivi.eval_json_result(data); + }); + } + + ns.massUploadInitialize = function() { + kivi.popup_dialog({ + id: 'status_mass_upload', + dialog: { + title: kivi.t8('Status Shopupload') + } + }); + }; + + ns.massUploadStarted = function() { + $('#status_mass_upload').data('timerId', setInterval(function() { + $.get("controller.pl", { + action: 'ShopPart/upload_status', + job_id: $('#smu_job_id').val() + }, kivi.eval_json_result); + }, 5000)); + }; + + ns.massUploadFinished = function() { + clearInterval($('#status_mass_upload').data('timerId')); + $('.ui-dialog-titlebar button.ui-dialog-titlebar-close').prop('disabled', '') + }; + + ns.imageUpload = function(id,type,filetype,upload_title,gl) { + kivi.popup_dialog({ url: 'controller.pl', + data: { action: 'File/ajax_upload', + file_type: filetype, + object_type: type, + object_id: id, + is_global: gl + }, + id: 'files_upload', + dialog: { title: kivi.t8('File upload'), width: 650, height: 240 } }); + return true; + } + + + ns.setup = function() { + kivi.ShopPart.massUploadInitialize(); + kivi.submit_ajax_form('controller.pl?action=ShopPart/mass_upload','[name=shop_parts]'); + }; + +}); diff --git a/templates/webpages/shop_part/_filter.html b/templates/webpages/shop_part/_filter.html new file mode 100644 index 000000000..3e49a0e90 --- /dev/null +++ b/templates/webpages/shop_part/_filter.html @@ -0,0 +1,28 @@ +[%- USE T8 %] +[%- USE L %] +[%- USE LxERP %] +[%- USE HTML %] +
+ [% L.hidden_tag('filter.shop.obsolete', 0) %] + + + + + + + + + + +
[% 'Shop' | $T8 %][% L.select_tag('filter.shop_id:eq_ignore_empty', SELF.shops, value_key = 'value', title_key = 'title', default=0) %]
[% 'Part marked as "Shop article"' %] + [% L.yes_no_tag('filter.part.shop', FORM.filter.part.shop, default='1', with_empty=1, empty_title='---') %]
+ +

+ [% 'Reset' | $T8 %] +
+

+

+ [% L.hidden_tag('action', 'ShopPart/dispatch') %] + [% L.submit_tag('action_list_articles',LxERP.t8('renew')) %] +

+
diff --git a/templates/webpages/shop_part/_list_articles.html b/templates/webpages/shop_part/_list_articles.html new file mode 100644 index 000000000..244fb767b --- /dev/null +++ b/templates/webpages/shop_part/_list_articles.html @@ -0,0 +1,136 @@ +[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%] +[% USE Dumper %] + + +

[% title %]

+[%- PROCESS 'shop_part/_filter.html' filter=SELF.models.filtered.laundered %] +
+
+
[% 'Number of Data: ' | $T8 %] [% SHOP_PARTS.size %]
+ + + + + + + + + + + + + + + + + + [%- FOREACH shop_part = SHOP_PARTS %] + [%- # IF shop_part.shop.obsolete %] + + + + + + + + + + + + + + + + [%- # END %] + [%- END %] +
[% L.checkbox_tag('check_all') %][% IF FORM.sort_by == 'shop.description' %] + + [% 'Shop Host/Connector' | $T8 %][% IF FORM.sort_dir == 0 %][% ELSE %][% END %] + [% ELSE %] + + [% 'Shop Host/Connector' | $T8 %] + [% END %] + [% IF FORM.sort_by == 'part.partnumber' %] + + [% 'Partnumber' | $T8 %][% IF FORM.sort_dir == 0 %][% ELSE %][% END %] + [% ELSE %] + + [% 'Partnumber' | $T8 %] + [% END %] + [% IF FORM.sort_by == 'part.description' %] + + [% 'Description' | $T8 %][% IF FORM.sort_dir == 0 %][% ELSE %][% END %] + [% ELSE %] + + [% 'Description' | $T8 %] + [% END %] + [% 'Info' | $T8 %][% 'Active' | $T8 %][% 'Price source' | $T8 %][% 'Price' | $T8 %][% IF FORM.sort_by == 'part.onhand' %] + + [% 'Stock Local/Shop' | $T8 %][% IF FORM.sort_dir == 0 %][% ELSE %][% END %] + [% ELSE %] + + [% 'Stock Local/Shop' | $T8 %] + [% END %] + [% 'Last update' | $T8 %][% 'Images' | $T8 %][% IF FORM.sort_by == 'part.partsgroup_id' %] + + [% 'Category' %][% IF FORM.sort_dir == 0 %][% ELSE %][% END %] + [% ELSE %] + + [% 'Category' | $T8 %] + [% END %] +
[% L.checkbox_tag('shop_parts_ids[]', checked=0, value=shop_part.id) %][% HTML.escape( shop_part.shop.description ) %]/[% HTML.escape( shop_part.shop.connector ) %][% HTML.escape( shop_part.part.partnumber ) %][% HTML.escape( shop_part.part.description ) %] + [% IF shop_part.shop_description %] + [% 'Info' | $T8 %] + [% ELSE %] + [% 'No Shopdescription' | $T8 %] + [% END %] + + [% IF shop_part.active %] +
 
+ [% ELSE %] +
 
+ [% END %] +
[% L.html_tag('span',LxERP.t8(), id => 'active_price_source_' _ shop_part.id) %] [% L.html_tag('span','Price', id => 'price_' _ shop_part.id) %][% L.html_tag('span','Stock', id => 'stock_' _ shop_part.id) %][% L.html_tag('span', shop_part.last_update.to_kivitendo('precision' => 'minute'), id => 'shop_part_last_update_' _ shop_part.id ) %] + [% IF shop_part.images %] + [% shop_part.images %] + [% ELSE %] + [% 'No Shopimages' | $T8 %] + [% END %] + + [% IF shop_part.shop_category %] + [% IF shop_part.shop_category.1.size > 1%] + [% FOREACH cat = shop_part.shop_category %] + [% HTML.escape(cat.1) %]
+ [% END %] + [% ELSE %] + [% HTML.escape(shop_part.shop_category.1) %]
+ [% END %] + [% END %] +
+ +
+
+ [% L.radio_button_tag('upload_todo', value='all', label= LxERP.t8('All Data')) %] + [% L.radio_button_tag('upload_todo', value='price', label= LxERP.t8('Only Price')) %] + [% L.radio_button_tag('upload_todo', value='stock', label= LxERP.t8('Only Stock')) %] + [% L.radio_button_tag('upload_todo', value='price_stock', checked=1, label= LxERP.t8('Price and Stock')) %] + [% L.button_tag("kivi.ShopPart.setup();", LxERP.t8("Upload all marked"), id="mass_transfer") %] +
+ +
+
+ diff --git a/templates/webpages/shop_part/_list_images.html b/templates/webpages/shop_part/_list_images.html new file mode 100644 index 000000000..6c811b346 --- /dev/null +++ b/templates/webpages/shop_part/_list_images.html @@ -0,0 +1,35 @@ +[%- USE HTML %][%- USE L -%][%- USE P -%][%- USE LxERP -%] +[%- USE T8 %][% USE Base64 %] +[%- USE Dumper %] + + + + + + + + + + + + + + [%- FOREACH img = IMAGES %] + [% # Dumper.dump_html(img) %] + + + + + + + + + + [% END %] + +
[%- LxERP.t8('reorder item') %][% 'Title' | $T8 %][% 'Description' | $T8 %][% 'Filename' | $T8 %][% 'Orig. Size w/h' | $T8 %][% 'Action' | $T8 %]
[%- LxERP.t8('reorder item') %][%  img.file.title %][% HTML.escape(img.file.title) %][% HTML.escape(img.file.description) %][% HTML.escape(img.file.file_name) %][% HTML.escape(img.org_file_width) _ ' x ' _ HTML.escape(img.org_file_height) %][% L.button_tag("kivi.File.delete_file(" _ img.file_id _ ", 'ShopPart/ajax_delete_file')", LxERP.t8('Delete'), confirm=LxERP.t8("Are you sure?")) %]
+ +[% L.sortable_element('#images_list tbody', url=SELF.url_for(action='reorder'), with='image_id') %] +

+[% L.button_tag("kivi.ShopPart.imageUpload(" _ FORM.id _ ",'shop_image','image', '',0);", LxERP.t8('File upload') ) %] +

diff --git a/templates/webpages/shop_part/_transfer_status.html b/templates/webpages/shop_part/_transfer_status.html new file mode 100644 index 000000000..bbee02b81 --- /dev/null +++ b/templates/webpages/shop_part/_transfer_status.html @@ -0,0 +1,52 @@ +[%- USE LxERP -%][%- USE L -%][%- USE HTML -%] +[%- USE Dumper -%] +[% SET data = job.data_as_hash %] + +

[% LxERP.t8("Watch status") %]

+ +[% L.hidden_tag('', job.id, id="smu_job_id") %] + +JOBID: [% job.id %]

+ [% LxERP.t8("This status output will be refreshed every five seconds.") %] +

+

+ + + + + + + + + + + + +
[% LxERP.t8("Current status:") %] + [% IF !data.status %] + [% LxERP.t8("waiting for job to be started") %] + [% ELSIF data.status == 1 %] + [% LxERP.t8("Uploading Data") %] + [% ELSE %] + [% LxERP.t8("Done.") %] + [% END %] +
[% LxERP.t8("Number of data uploaded:") %][% IF data.status > 0 %][% HTML.escape(data.num_uploaded) %] / [% HTML.escape(data.record_ids.size) %][% ELSE %]–[% END %]
[% LxERP.t8("Conversion:") %] +[% IF !data.status %] + – + + + + + + + + [% FOREACH message = data.conversion %] + + + + + [% END %] +
[% LxERP.t8("Part") %][% LxERP.t8("Partnumber") %][% LxERP.t8("Message") %]
[% IF error.id %][% L.link(SELF.url_for(controller='ShopOrder', action='show', id=error.id), HTML.escape(error.number), target="_blank") %][% ELSE %]–[% END %][% HTML.escape(message.message) %]
+ [% END %] +
+

diff --git a/templates/webpages/shop_part/_upload_status.html b/templates/webpages/shop_part/_upload_status.html new file mode 100644 index 000000000..e7285a24a --- /dev/null +++ b/templates/webpages/shop_part/_upload_status.html @@ -0,0 +1,51 @@ +[%- USE LxERP -%][%- USE L -%][%- USE HTML -%] +[%- USE Dumper -%] +[% SET data = job.data_as_hash %] + +

[% LxERP.t8("Watch status") %]

+ +[% L.hidden_tag('', job.id, id="smu_job_id") %] + +JOBID: [% job.id %]

+ [% LxERP.t8("This status output will be refreshed every five seconds.") %] +

+

+ + + + + + + + + + + + +
[% LxERP.t8("Current status:") %] + [% IF !data.status %] + [% LxERP.t8("waiting for job to be started") %] + [% ELSIF data.status == 1 %] + [% LxERP.t8("Uploading Data") %] + [% ELSE %] + [% LxERP.t8("Done.") %] + [% END %] +
[% LxERP.t8("Number of data uploaded:") %][% IF data.status > 0 %][% HTML.escape(data.num_uploaded) %] / [% HTML.escape(data.shop_part_record_ids.size) %][% ELSE %]–[% END %]
[% LxERP.t8("Conversion:") %] + – + + + + + + + + [% FOREACH message = data.conversion %] + + + + + + [% END %] +
[% LxERP.t8("Part") %][% LxERP.t8("Partnumber") %][% LxERP.t8("Message") %]
[% HTML.escape(message.id) %][% HTML.escape(message.number) %][% HTML.escape(message.message) %]
+
+

diff --git a/templates/webpages/shop_part/categories.html b/templates/webpages/shop_part/categories.html new file mode 100644 index 000000000..80676eb9b --- /dev/null +++ b/templates/webpages/shop_part/categories.html @@ -0,0 +1,55 @@ +[%- USE HTML %] +[%- USE T8 %] +[%- USE L -%] +[%- USE P -%] +[%- USE LxERP -%] +[%- USE Dumper -%] + +[% LxERP.t8("Part") %]: [% HTML.escape(SELF.shop_part.part.displayable_name) %]
+[% LxERP.t8("Shop") %]: [% HTML.escape(SELF.shop_part.shop.description) %] +
+[% # Dumper.dump_html(SELF.shop_part.shop_category) %] +
+[% # Dumper.dump_html(CATEGORIES) %] + +
+ [% BLOCK recurse %] + [% # path = '' %] + [% FOREACH obj = data %] +
    +
  • + [% checked = '' %] + [% # path = path _ obj.name %] + [% # test = path.split('\|') %] + [% # Dumper.dump_html(SELF.shop_part.shop_category) %] + [% IF SELF.shop_part.shop_category.1.grep(obj.name).size %] + [% checked = 'checked' %] + [% ELSE %] + [% FOREACH cat_row = SELF.shop_part.shop_category %] + [% IF cat_row.1.grep(obj.name).size %] + [% checked = 'checked' %] + [% END %] + [% END %] + [% END %] + [% L.checkbox_tag('categories[]',value=obj.id, checked=checked) %][% HTML.escape(obj.name) %][% L.hidden_tag("cat_id_" _ obj.id, obj.name) %]
  • + [% IF obj.childrenCount >= 1 %] + [% # path = path _ '|' %] + [% INCLUDE recurse data=obj.children %] + [% END %] +
+ [% END %] + [% END %] +

[% LxERP.t8("Shopcategories") %]

+ [% FOREACH row = CATEGORIES %] + + [% IF row.name == 'Root' %] + [% IF row.childrenCount >= 1 %] + [% path = '' %] + [% INCLUDE recurse data=row.children path=path %] + [% END %] + [% END %] + [% END %] +
+ [% L.button_tag("kivi.ShopPart.save_categories(" _ SELF.shop_part.id _", " _ SELF.shop_part.shop.id _")", LxERP.t8("Save")) %] +
+ diff --git a/templates/webpages/shop_part/edit.html b/templates/webpages/shop_part/edit.html new file mode 100644 index 000000000..418a632da --- /dev/null +++ b/templates/webpages/shop_part/edit.html @@ -0,0 +1,81 @@ +[%- USE HTML %] +[%- USE T8 %] +[%- USE L -%] +[%- USE P -%] +[%- USE LxERP -%] +[%- USE Dumper -%] + +

+[% LxERP.t8("Part") %]: [% HTML.escape(SELF.shop_part.part.displayable_name) %]
+[% LxERP.t8("Shop") %]: [% HTML.escape(SELF.shop_part.shop.description) %] +

+[% # Dumper.dump_html(SELF) %] +

+
+ [% IF SELF.shop_part.id %] + [%- L.hidden_tag("shop_part.id", SELF.shop_part.id) %] + [%- L.hidden_tag("shop_part.shop_id", SELF.shop_part.shop_id) %] + [% ELSE %] + [%- L.hidden_tag("shop_part.shop_id", FORM.shop_id) %] + [%- L.hidden_tag("shop_part.part_id", FORM.part_id) %] + [% END %] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
[% LxERP.t8("Description") %][% L.textarea_tag('shop_part.shop_description', SELF.shop_part.shop_description, wrap="soft", style="width: 350px; height: 150px", class="texteditor") %]
[% LxERP.t8("Active") %][% L.yes_no_tag("shop_part.active", SELF.shop_part.active, default = "yes") %][% LxERP.t8("Date") %][% L.date_tag("shop_part.show_date", SELF.shop_part.show_date) %]
[% 'Price Source' | $T8 %] + [% IF SELF.shop_part.active_price_source %] + [% SET price_source = SELF.shop_part.active_price_source %] + [% ELSE %] + [% SET price_source = SELF.shop_part.shop.price_source %] + [% END %] + [% L.select_tag('shop_part.active_price_source', SELF.price_sources, value_key = 'id', title_key = 'name', with_empty = 0, default = price_source, default_value_key='id' ) %][% LxERP.t8("Front page") %][% L.yes_no_tag('shop_part.front_page', SELF.shop_part.front_page) %]
[% LxERP.t8("Sort order") %][% L.input_tag("shop_part.sortorder", SELF.shop_part.sortorder, size=2) %][% LxERP.t8("Meta tag title") %][% L.input_tag("shop_part.metatag_title", SELF.shop_part.metatag_title, size=12) %]
[% LxERP.t8("Meta tag keywords") %][% L.input_tag("shop_part.metatag_keywords", SELF.shop_part.metatag_keywords, size=22) %][% LxERP.t8("Meta tag description") %][% L.textarea_tag("shop_part.metatag_description", SELF.shop_part.metatag_description, rows=4) %]
+ [% # L.dump(SELF.shop_part) %] + + [% IF SELF.shop_part.id %] + [% L.button_tag("kivi.ShopPart.save_shop_part(" _ SELF.shop_part.id _ ")", LxERP.t8("Save")) %] + [% ELSE %] + [% L.button_tag("kivi.ShopPart.add_shop_part(" _ FORM.part_id _", " _ FORM.shop_id _")", LxERP.t8("Save")) %] + [% END %] + [% # L.button_tag("kivi.ShopPart.update_partnumber()", LxERP.t8("Update Partnumber")) %] + + [% # L.hidden_tag("action", "ShopPart/dispatch") %] + [% # L.submit_tag('action_update', LxERP.t8('Save')) %] + + +
+
+ + + +[%- IF SELF.shop_part.part.image && INSTANCE_CONF.get_parts_show_image %] + +[%- END %] + +[% # SELF.shop_part.shop_description %] +[% # L.dump(SELF.shop_part) %] -- 2.20.1