Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 9f211141

Von Kivitendo Admin vor mehr als 7 Jahren hinzugefügt

  • ID 9f211141a002bd2eca2d17d1c17d0389779313db
  • Vorgänger f533d42b
  • Nachfolger a3329b6b

Shopmodul: Shop und ShopParts

Shop - Einführung von ShopParts

Erster Test mit einem Shop Reiter in Artikelmaske

Conflicts:
SL/IC.pm

Conflicts:
SL/DB/MetaSetup/ShopPart.pm
templates/webpages/ic/tabs/_shop.html

Shop - Rose shop_parts Verknüpfungen von Shop und Part

Conflicts:
SL/DB/Shop.pm

Shop - SL/Shop and Shop Controller for editing shop configs

Conflicts:
SL/DB/Shop.pm

Conflicts:
SL/DB/Helper/ALL.pm
SL/DB/Helper/Mappings.pm

Unterschiede anzeigen:

SL/Controller/Shop.pm
1
package SL::Controller::Shop;
2

  
3
use strict;
4

  
5
use parent qw(SL::Controller::Base);
6

  
7
use SL::Helper::Flash;
8
use SL::Locale::String;
9
use SL::DB::Default;
10
use SL::DB::Manager::Shop;
11
use SL::DB::Pricegroup;
12

  
13
use Rose::Object::MakeMethods::Generic (
14
  scalar                  => [ qw(connectors price_types price_sources) ],
15
  'scalar --get_set_init' => [ qw(shop) ]
16
);
17

  
18
__PACKAGE__->run_before('check_auth');
19
__PACKAGE__->run_before('load_types',    only => [ qw(new edit) ]);
20

  
21
#
22
# actions
23
#
24

  
25
sub action_list {
26
  my ($self) = @_;
27

  
28
  $self->render('shops/list',
29
                title => t8('Shops'),
30
                SHOPS => SL::DB::Manager::Shop->get_all_sorted,
31
               );
32
}
33

  
34
sub action_new {
35
  my ($self) = @_;
36

  
37
  $self->shop(SL::DB::Shop->new);
38
  $self->render('shops/form', title       => t8('Add shop'));
39
};
40

  
41
sub action_edit {
42
  my ($self) = @_;
43

  
44
  $self->render('shops/form', title       => t8('Edit shop'));
45
}
46

  
47
sub action_create {
48
  my ($self) = @_;
49

  
50
  $self->shop(SL::DB::Shop->new);
51
  $self->create_or_update;
52
}
53

  
54
sub action_update {
55
  my ($self) = @_;
56
  $self->create_or_update;
57
}
58

  
59
sub action_delete {
60
  my ($self) = @_;
61

  
62
  if ( eval { $self->shop->delete; 1; } ) {
63
    flash_later('info',  $::locale->text('The shop has been deleted.'));
64
  } else {
65
    flash_later('error', $::locale->text('The shop has been used and cannot be deleted.'));
66
  };
67
  $self->redirect_to(action => 'list');
68
}
69

  
70
sub action_reorder {
71
  my ($self) = @_;
72

  
73
  SL::DB::Shop->reorder_list(@{ $::form->{shop_id} || [] });
74
  $self->render(\'', { type => 'json' });
75
}
76

  
77
#
78
# filters
79
#
80

  
81
sub check_auth {
82
  $::auth->assert('config');
83
}
84

  
85
sub init_shop {
86
  SL::DB::Shop->new(id => $::form->{id})->load;
87
}
88

  
89
#
90
# helpers
91
#
92

  
93
sub create_or_update {
94
  my ($self) = @_;
95
  my $is_new = !$self->shop->id;
96

  
97
  my $params = delete($::form->{shop}) || { };
98

  
99
  $self->shop->assign_attributes(%{ $params });
100

  
101
  my @errors = $self->shop->validate;
102

  
103
  if (@errors) {
104
    flash('error', @errors);
105
    $self->render('shops/form',
106
                   title => $is_new ? t8('Add shop') : t8('Edit shop'));
107
    return;
108
  }
109

  
110
  $self->shop->save;
111

  
112
  flash_later('info', $is_new ? t8('The shop has been created.') : t8('The shop has been saved.'));
113
  $self->redirect_to(action => 'list');
114
}
115

  
116
sub load_types {
117
  my ($self) = @_;
118
  # data for the dropdowns when editing Shop configs
119

  
120
  # hardcoded the possible connectors, which correspond to
121
  # SL/ShopConnector/xxxx classes
122
  $self->connectors( [ { id => "xtcommerce", description => "XT Commerce"},
123
                       { id => "shopware",   description => "Shopware" },
124
                       { id => "ideal",      description => "IDeal" }
125
                     ]);
126

  
127
  # whether the shop presents its prices as brutto or netto
128
  $self->price_types( [ { id => "brutto", name => t8('brutto')}, { id => "netto", name => t8('netto') } ] );
129

  
130
  # the possible price sources to use for the shops: sellprice, lastcost,
131
  # listprice, or one of the pricegroups
132
  my $pricesources;
133
  push( @{ $pricesources } , { id => "sellprice", name => t8("Sellprice") },
134
                             { id => "listprice", name => t8("Listprice") },
135
                             { id => "lastcost",  name => t8("Lastcost") }
136
                             );
137
  my $pricegroups = SL::DB::Manager::Pricegroup->get_all;
138
  foreach my $pg ( @$pricegroups ) {
139
    push( @{ $pricesources } , { id => $pg->id, name => $pg->pricegroup} );
140
  };
141

  
142
  $self->price_sources( $pricesources );
143
};
144

  
145

  
146
1;
SL/DB/Helper/ALL.pm
115 115
use SL::DB::SepaExportItem;
116 116
use SL::DB::SepaExportMessageId;
117 117
use SL::DB::Shipto;
118
use SL::DB::Shop;
119
use SL::DB::ShopPart;
118 120
use SL::DB::Status;
119 121
use SL::DB::Tax;
120 122
use SL::DB::TaxKey;
SL/DB/Helper/Mappings.pm
195 195
  sepa_export_message_ids        => 'SepaExportMessageId',
196 196
  schema_info                    => 'schema_info',
197 197
  shipto                         => 'shipto',
198
  shops                          => 'shop',
199
  shop_parts                     => 'shop_part',
198 200
  status                         => 'status',
199 201
  tax                            => 'tax',
200 202
  taxkeys                        => 'tax_key',
......
208 210
  units_language                 => 'units_language',
209 211
  user_preferences               => 'user_preference',
210 212
  vendor                         => 'vendor',
213
  web_shops                      => 'web_shop',
211 214
  warehouse                      => 'warehouse',
212 215
);
213 216

  
SL/DB/Manager/Shop.pm
1
# This file has been auto-generated only because it didn't exist.
2
# Feel free to modify it at will; it will not be overwritten automatically.
3

  
4
package SL::DB::Manager::Shop;
5

  
6
use strict;
7

  
8
use SL::DB::Helper::Manager;
9
use base qw(SL::DB::Helper::Manager);
10

  
11
sub object_class { 'SL::DB::Shop' }
12

  
13
use SL::DB::Helper::Sorted;
14

  
15
__PACKAGE__->make_manager_methods;
16

  
17
sub _sort_spec {
18
  return ( default => [ 'sortkey', 1 ],
19
           columns => { SIMPLE => 'ALL' } );
20
}
21

  
22
sub get_default {
23
    return $_[0]->get_first(where => [ obsolete => 0 ], sort_by => 'sortkey');
24
}
25

  
26
1;
27

  
28
1;
SL/DB/MetaSetup/Shop.pm
1
# This file has been auto-generated. Do not modify it; it will be overwritten
2
# by rose_auto_create_model.pl automatically.
3
package SL::DB::Shop;
4

  
5
use strict;
6

  
7
use base qw(SL::DB::Object);
8

  
9
__PACKAGE__->meta->table('shops');
10

  
11
__PACKAGE__->meta->columns(
12
  connector    => { type => 'text' },
13
  description  => { type => 'text' },
14
  id           => { type => 'serial', not_null => 1 },
15
  login        => { type => 'text' },
16
  obsolete     => { type => 'boolean', default => 'false', not_null => 1 },
17
  password     => { type => 'text' },
18
  port         => { type => 'integer' },
19
  price_source => { type => 'text' },
20
  pricetype    => { type => 'text' },
21
  sortkey      => { type => 'integer' },
22
  url          => { type => 'text' },
23
);
24

  
25
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
26

  
27
1;
28
;
SL/DB/MetaSetup/ShopPart.pm
1
# This file has been auto-generated. Do not modify it; it will be overwritten
2
# by rose_auto_create_model.pl automatically.
3
package SL::DB::ShopPart;
4

  
5
use strict;
6

  
7
<<<<<<< HEAD
8
use parent qw(SL::DB::Object);
9
=======
10
use base qw(SL::DB::Object);
11
>>>>>>> Shop - Einführung von ShopParts
12

  
13
__PACKAGE__->meta->table('shop_parts');
14

  
15
__PACKAGE__->meta->columns(
16
  active              => { type => 'boolean', default => 'false', not_null => 1 },
17
  active_price_source => { type => 'text' },
18
  front_page          => { type => 'boolean', default => 'false', not_null => 1 },
19
  id                  => { type => 'serial', not_null => 1 },
20
  itime               => { type => 'timestamp', default => 'now()' },
21
  last_update         => { type => 'timestamp' },
22
  metatag_description => { type => 'text' },
23
  metatag_keywords    => { type => 'text' },
24
  metatag_title       => { type => 'text' },
25
  mtime               => { type => 'timestamp' },
26
  part_id             => { type => 'integer', not_null => 1 },
27
  shop_category       => { type => 'array' },
28
  shop_description    => { type => 'text' },
29
  shop_id             => { type => 'integer', not_null => 1 },
30
  show_date           => { type => 'date' },
31
  sortorder           => { type => 'integer' },
32
);
33

  
34
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
35

  
36
__PACKAGE__->meta->unique_keys([ 'shop_id', 'part_id' ]);
37

  
38
__PACKAGE__->meta->allow_inline_column_values(1);
39

  
40
__PACKAGE__->meta->foreign_keys(
41
  part => {
42
    class       => 'SL::DB::Part',
43
    key_columns => { part_id => 'id' },
44
  },
45

  
46
  shop => {
47
    class       => 'SL::DB::Shop',
48
    key_columns => { shop_id => 'id' },
49
  },
50
);
51

  
52
1;
53
;
SL/DB/Part.pm
29 29
    type         => 'one to many',
30 30
    class        => 'SL::DB::Price',
31 31
    column_map   => { id => 'parts_id' },
32
    manager_args => { with_objects => [ 'pricegroup' ] }
32 33
  },
33 34
  makemodels     => {
34 35
    type         => 'one to many',
......
53 54
    query_args      => [ what_done => 'part' ],
54 55
    manager_args    => { sort_by => 'itime' },
55 56
  },
57
  shop_parts     => {
58
    type         => 'one to many',
59
    class        => 'SL::DB::ShopPart',
60
    column_map   => { id => 'part_id' },
61
  },
56 62
);
57 63

  
58 64
__PACKAGE__->meta->initialize;
SL/DB/Shop.pm
1
# This file has been auto-generated only because it didn't exist.
2
# Feel free to modify it at will; it will not be overwritten automatically.
3

  
4
package SL::DB::Shop;
5

  
6
use strict;
7

  
8
use SL::DB::MetaSetup::Shop;
9
use SL::DB::Manager::Shop;
10
use SL::DB::Helper::ActsAsList;
11

  
12
__PACKAGE__->meta->add_relationships(
13
  shop_parts     => {
14
    type         => 'one to many',
15
    class        => 'SL::DB::ShopPart',
16
    column_map   => { id => 'shop_id' },
17
  },
18
);
19

  
20
__PACKAGE__->meta->initialize;
21

  
22
sub validate {
23
  my ($self) = @_;
24

  
25
  my @errors;
26

  
27
  push @errors, $::locale->text('The description is missing.') unless $self->{description};
28

  
29
  return @errors;
30
}
31
1;
SL/Shop.pm
1
package SL::Shop;
2

  
3
use strict;
4

  
5
use parent qw(Rose::Object);
6

  
7
# __PACKAGE__->run_before('check_auth');
8

  
9
use Rose::Object::MakeMethods::Generic (
10
  'scalar'                => [ qw(config) ],
11
  'scalar --get_set_init' => [ qw(connector) ],
12
);
13

  
14
sub init_connector {
15
  my ($self) = @_;
16
  # determine the connector from the connector type in the webshop config
17
  return SL::ShopConnector::ALL->shop_connector_class_by_name($self->config->connector)->new( config => $self->config); 
18

  
19
};
20

  
21
1;
22

  
23
__END__
24

  
25
=encoding utf8
26

  
27
=head1 NAME
28

  
29
SL::WebShop - Do stuff with WebShop instances
30

  
31
=head1 SYNOPSIS
32

  
33
  my $config = SL::DB::Manager::WebShop->get_first();
34
  my $shop = SL::WebShop->new( config => $config );
35

  
36
From the config we know which Connector class to load, save in $shop->connector
37
and do stuff from there:
38

  
39
  $shop->connector->get_new_orders;
40

  
41
=head1 FUNCTIONS
42

  
43
=head1 BUGS
44

  
45
Nothing here yet.
46

  
47
=head1 AUTHOR
48

  
49
G. Richardson <lt>information@kivitendo-premium.deE<gt>
50

  
51
=cut
52

  
menus/user/00-erp.yaml
1242 1242
  module: am.pl
1243 1243
  params:
1244 1244
    action: list_warehouses
1245
- parent: system
1246
  id: system_shops
1247
  name: Web shops
1248
  order: 2350
1249
  params:
1250
    action: Shop/list
1245 1251
- parent: system
1246 1252
  id: system_import_csv
1247 1253
  name: Import CSV
sql/Pg-upgrade2/shops.sql
1
-- @tag: shops
2
-- @description: Tabelle für Shops
3
-- @depends: release_3_3_0
4
-- @ignore: 0
5

  
6
CREATE TABLE shops (
7
  id SERIAL PRIMARY KEY,
8
  description text,
9
  obsolete BOOLEAN NOT NULL DEFAULT false,
10
  sortkey INTEGER,
11
  connector text,     -- hardcoded options, e.g. xtcommerce, shopware
12
  pricetype text,     -- netto/brutto
13
  price_source text,  -- sellprice/listprice/lastcost or pricegroup id
14
  url text,
15
  port INTEGER,
16
  login text,  -- "user" is reserved
17
  password text
18
);
templates/webpages/ic/tabs/_shop.html
1
[%- USE HTML %][%- USE L -%][%- USE P -%][%- USE LxERP -%]
2
<<<<<<< HEAD
3
[%- USE Dumper %]
4
[%- USE JavaScript -%]
5
<div id="shop_variables">
6
 <h2>[% LxERP.t8("Active shops:") %]</h2>
7
 <table width="100%">
8
  <thead>
9
  <tr class="listheading">
10
   <th>[% LxERP.t8("Shop") %]</th>
11
   <th>[% LxERP.t8("Active") %]</th>
12
   <th>[% LxERP.t8("Shop part") %]</th>
13
   <th>[% LxERP.t8("Price source") %]</th>
14
   <th>[% LxERP.t8("Price") %]</th>
15
   <th>[% LxERP.t8("Stock Local/Shop") %]</th>
16
   <th>[% LxERP.t8("Last update") %]</th>
17
   <th>[% LxERP.t8("Action") %]</th>
18
   <th>[% LxERP.t8("Action") %]</th>
19
   <th>[% LxERP.t8("Action") %]</th>
20
  </tr>
21
  </thead>
22
  [%- FOREACH shop_part = SHOP_PARTS %]
23
  <tr class="listrow">
24
   <td>[% HTML.escape( shop_part.shop.description ) %]</td>
25
   <td>[% L.html_tag('span', shop_part.active, id => 'shop_part_active_' _ shop_part.id ) %]</td>
26
   <td>[% L.html_tag('span', shop_part.shop_description, id => 'shop_part_description_' _ shop_part.id ) %]</td>
27
   <td> [% L.html_tag('span',LxERP.t8(), id => 'active_price_source_' _ shop_part.id) %] </td>
28
   <td>[% L.html_tag('span','Price', id => 'price_' _ shop_part.id) %]</td>
29
   <td>[% L.html_tag('span','Stock', id => 'stock_' _ shop_part.id) %]</td>
30
   <td>[% L.html_tag('span', shop_part.last_update.to_kivitendo('precision' => 'minute'), id => 'shop_part_last_update_' _ shop_part.id ) %]</td>
31
   <td>[% L.button_tag("kivi.shop_part.edit_shop_part(" _ shop_part.id _ ")", LxERP.t8("Edit"))  %]</td>
32
   <td>[% L.button_tag("kivi.shop_part.update_shop_part(" _ shop_part.id _ ")", LxERP.t8("Upload"))  %]</td>
33
   <td>[% L.button_tag("kivi.shop_part.get_all_categories(" _ shop_part.id _ ")", LxERP.t8("Shopcategories"))  %]</td>
34
  </tr>
35
  <script type="text/javascript">
36
    $(function() {
37
      kivi.shop_part.update_price_n_price_source([% shop_part.id %],'[% shop_part.active_price_source %]');
38
      kivi.shop_part.update_stock([% shop_part.id %]);
39
    });
40
  </script>
41
  [%- END %]
42
  [%- FOREACH shop = SHOPS_NOT_ASSIGNED %]
43
  <tr>
44
   <td>[% HTML.escape( shop.description ) %]</td>
45
   <td></td>
46
   <td></td>
47
   <td></td>
48
   <td>[% L.button_tag("kivi.shop_part.create_shop_part(" _ id _ ", " _ shop.id _ ")", LxERP.t8("Add"))  %]</td>
49
   <td></td>
50
  </tr>
51
  [%- END %]
52
</table>
53

  
54

  
55
[% # L.dump(shop_part) %]
56
<h2>[% LxERP.t8("Shopvariables and Images - valid for all shops") %]</h2>
57
  [%- IF CUSTOM_VARIABLES.size %]
58
[%  # L.dump(CUSTOM_VARIABLES) %]
59
  <div id="shop_custom_variables">
60
   <p>
61
    <table>
62
     [%- FOREACH var = CUSTOM_VARIABLES %]
63
      [%- IF var.name.match('^shop_') %]
64
       <tr>
65
        [% # IF !var.partsgroup_filtered %]
66
          <td align="right" valign="top">[% HTML.escape(var.description) %]</td>
67
        [% # END %]
68
      <td align="right" valign="top">[% var.VALID_BOX %]</td>
69
        <td valign="top">[% var.HTML_CODE %]</td>
70
       </tr>
71
      [%- END %]
72
     [%- END %]
73
    </table>
74
   </p>
75
  </div>
76
  [%- END %]
77
  <script type="text/javascript">
78
    $(function() {
79
      kivi.shop_part.show_images([% shop_part.part_id %]);
80
    });
81
  </script>
82
  <div id="shop_images" border=1 >
83
  </div>
84
</div>
85

  
templates/webpages/shops/form.html
1
[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%]
2

  
3
[% SET style="width: 400px" %]
4
[% SET size=34 %]
5

  
6
<h1>[% HTML.escape(title) %]</h1>
7

  
8
<form action="controller.pl" method="post">
9

  
10
[%- INCLUDE 'common/flash.html' %]
11

  
12
[%- L.hidden_tag("id", SELF.shop.id) %]
13

  
14
<table>
15
  <tr>
16
    <th align="right">[% 'Description' | $T8 %]</th>
17
    <td>[%- L.input_tag("shop.description", SELF.shop.description, size=size) %]</td>
18
  </tr>
19
  <tr>
20
    <th align="right">[% 'Shop type' | $T8 %]</th>
21
    <td>[% L.select_tag('shop.connector', SELF.connectors, value_key = 'id', title_key = 'description', with_empty = 1, default = SELF.shop.connector, default_value_key='id' ) %]</td>
22
  <tr>
23
  <tr>
24
    <th align="right">[% 'Price type' | $T8 %]</th>
25
    <td>[% L.select_tag('shop.pricetype', SELF.price_types, value_key = 'id', title_key = 'name', with_empty = 1, default = SELF.shop.pricetype, default_value_key='id' ) %]</td>
26
  </tr>
27
  <tr>
28
    <th align="right">[% 'Price Source' | $T8 %]</th>
29
    <td>[% L.select_tag('shop.price_source', SELF.price_sources, value_key = 'id', title_key = 'name', with_empty = 1, default = SELF.shop.price_source, default_value_key='id' ) %]</td>
30
  </tr>
31
  <tr>
32
    <th align="right">[% 'URL' | $T8 %]</th>
33
    <td>[%- L.input_tag("shop.url", SELF.shop.url, size=size) %]</td>
34
  </tr>
35
  <tr>
36
    <th align="right">[% 'Port' | $T8 %]</th>
37
    <td>[%- L.input_tag("shop.port", SELF.shop.port, size=5) %]</td>
38
   <tr>
39
    <th align="right">[% 'User' | $T8 %]</th>
40
    <td>[%- L.input_tag("shop.login", SELF.shop.login, size=12) %]</td>
41
  </tr>
42
    <tr>
43
    <th align="right">[% 'Password' | $T8 %]</th>
44
    <td>[%- L.input_tag("shop.password", SELF.shop.password, size=12) %]</td>
45
  </tr>
46
 </tr>
47
  <tr>
48
    <th align="right">[% 'Obsolete' | $T8 %]</th>
49
    <td>[% L.checkbox_tag('shop.obsolete', checked = SELF.shop.obsolete, for_submit=1) %]</td>
50
  </tr>
51
</table>
52

  
53
 <p>
54
  [% L.hidden_tag("action", "Shop/dispatch") %]
55
  [% L.submit_tag("action_" _  (SELF.shop.id ? "update" : "create"), LxERP.t8('Save'), onclick="return check_prerequisites();") %]
56
  [%- IF SELF.shop.id -%]
57
    [% L.submit_tag("action_delete", LxERP.t8('Delete')) %]
58
  [%- END %]
59
  <a href="[% SELF.url_for(action='list') %]">[%- LxERP.t8("Cancel") %]</a>
60
 </p>
61

  
62
 <hr>
63

  
64
<script type="text/javascript">
65
<!--
66
function check_prerequisites() {
67
  if ($('#shop_description').val() === "") {
68
    alert(kivi.t8('The name is missing.'));
69
    return false;
70
  }
71
  if ($('#shop_url').val() === "") {
72
    alert(kivi.t8('The URL is missing.'));
73
    return false;
74
  }
75
  if ($('#shop_port').val() === "") {
76
    alert(kivi.t8('The port is missing.'));
77
    return false;
78
  }
79

  
80
  return true;
81
}
82
-->
83
</script>
84
</form>
templates/webpages/shops/list.html
1
[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%][%- INCLUDE 'common/flash.html' %]
2

  
3
<h1>[% title %]</h1>
4

  
5
<p>
6
 <table width="100%" id="shop_list">
7
  <thead>
8
   <tr class="listheading">
9
    <th align="center" width="1%"><img src="image/updown.png" alt="[ LxERP.t8('reorder item') %]"></th>
10
    <th>[% 'Description' | $T8 %]</th>
11
    <th>[% 'Type' | $T8 %]</th>
12
   </tr>
13
  </thead>
14

  
15
  <tbody>
16
   [%- FOREACH shop = SHOPS %]
17
    <tr class="listrow" id="shop_id_[% shop.id %]">
18
     <td align="center" class="dragdrop"><img src="image/updown.png" alt="[ LxERP.t8('reorder item') %]"></td>
19
     <td><a href="[% SELF.url_for(action='edit', id=shop.id) %]">[% HTML.escape(shop.description) %]</a></td>
20
     <td>[% HTML.escape(shop.connector) %]</a></td>
21
    </tr>
22
   [%- END %]
23
  </tbody>
24
 </table>
25
</p>
26

  
27
<hr height="3">
28

  
29
[% L.sortable_element('#shop_list tbody', url=SELF.url_for(action='reorder'), with='shop_id') %]
30

  
31
<p>
32
 <a href="[% SELF.url_for(action='new') %]">[%- 'Add' | $T8 %]</a>
33
</p>

Auch abrufbar als: Unified diff