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 %]
+
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 %]
+
+
+
+
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 %]
+
+
+
+ |
+ |
+ [% 'Title' | $T8 %] |
+ [% 'Description' | $T8 %] |
+ [% 'Filename' | $T8 %] |
+ [% 'Orig. Size w/h' | $T8 %] |
+ [% 'Action' | $T8 %] |
+
+
+
+ [%- FOREACH img = IMAGES %]
+ [% # Dumper.dump_html(img) %]
+
+ |
+ |
+ [% 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?")) %] |
+
+ [% END %]
+
+
+
+[% 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 %]
+
+ [% 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 %]
+
+ [% 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 %]
+
+ [% HTML.escape(message.id) %] |
+ [% HTML.escape(message.number) %] |
+ [% HTML.escape(message.message) %] |
+
+ [% END %]
+
+ |
+
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) %]
+
+
+
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.part.image && INSTANCE_CONF.get_parts_show_image %]
+
+[%- END %]
+
+[% # SELF.shop_part.shop_description %]
+[% # L.dump(SELF.shop_part) %]
--
2.20.1