View | Details | Raw Unified | Return to bug 11897
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/stockrotation-toolbar.inc (+13 lines)
Line 0 Link Here
1
[% USE Koha %]
2
<div id="toolbar" class="btn-toolbar">
3
    [% IF no_op_set %]
4
        <a id="addrota" class="btn btn-small" href="/cgi-bin/koha/tools/stockrotation.pl?op=create_edit_rota"><i class="fa fa-plus"></i> New rota</a>
5
    [% END %]
6
    [% IF op == 'manage_stages' %]
7
        <a id="addstage" class="btn btn-small" href="/cgi-bin/koha/tools/stockrotation.pl?op=create_edit_stage&amp;rota_id=[% rota_id %]"><i class="fa fa-plus"></i> New stage</a>
8
    [% END %]
9
    [% IF op == 'manage_items' %]
10
        <a id="addrota" class="btn btn-small" href="/cgi-bin/koha/tools/stockrotation.pl?op=create_edit_rota"><i class="fa fa-plus"></i> New rota</a>
11
        <a id="editrota" class="btn btn-small" href="/cgi-bin/koha/tools/stockrotation.pl?op=create_edit_rota&amp;rota_id=[% rota_id %]"><i class="fa fa-pencil"></i> Edit rota</a>
12
    [% END %]
13
</div>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/stockrotation.tt (+482 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% USE Branches %]
3
[% USE KohaDates %]
4
[% INCLUDE 'doc-head-open.inc' %]
5
<title>Koha &rsaquo; Stock rotation</title>
6
[% INCLUDE 'doc-head-close.inc' %]
7
<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/datatables.css" />
8
[% INCLUDE 'datatables.inc' %]
9
<script type="text/javascript" src="[% interface %]/[% theme %]/js/pages/stockrotation.js"></script>
10
<script type="text/javascript">
11
//<![CDATA[
12
    $(document).ready(function() {
13
        $('#sr_manage_items').dataTable($.extend(true, {}, dataTablesDefaults, {
14
            "autoWidth": false,
15
            "aoColumnDefs": [
16
                { "bSortable": false, "bSearchable": false, 'aTargets': [ 'NoSort' ] },
17
                { "bSortable": true, "bSearchable": false, 'aTargets': [ 'NoSearch' ] }
18
            ],
19
            "sPaginationType": "four_button"
20
        }));
21
    });
22
//]]>
23
</script>
24
</head>
25
26
<body>
27
[% INCLUDE 'header.inc' %]
28
[% INCLUDE 'patron-search.inc' %]
29
30
<div id="breadcrumbs">
31
    <a href="/cgi-bin/koha/mainpage.pl">Home</a>
32
    &rsaquo; <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a>
33
34
[% IF no_op_set %]
35
    &rsaquo; Stock rotation
36
[% ELSE %]
37
    &rsaquo; <a href="/cgi-bin/koha/tools/stockrotation.pl">Stock rotation</a>
38
[% END %]
39
40
[% IF (op == 'create_edit_rota' && rota.rota_id) %]
41
    &rsaquo; Edit rota
42
[% ELSIF (op == 'create_edit_rota' && !rota.rota_id) %]
43
    &rsaquo; Create rota
44
[% ELSIF (op == 'manage_stages') %]
45
    &rsaquo; Manage stages
46
[% ELSIF (op == 'create_edit_stage' && stage.id) %]
47
    <a href="?op=manage_stages&amp;rota_id=[% rota_id %]">&rsaquo; Manage stages</a>
48
    &rsaquo; Edit stage
49
[% ELSIF (op == 'create_edit_stage' && !stage.id) %]
50
    <a href="?op=manage_stages&amp;rota_id=[% rota_id %]">&rsaquo; Manage stages</a>
51
    &rsaquo; Create stage
52
[% ELSIF (op == 'manage_items') %]
53
    &rsaquo; Manage items
54
[% END %]
55
56
</div>
57
58
<div id="doc3" class="yui-t2">
59
    <div id="bd">
60
        <div id="yui-main">
61
            <div id="stockrotation" class="yui-b">
62
63
                <h1>Stock rotation</h1>
64
65
                [% IF no_op_set %]
66
67
                    [% INCLUDE 'stockrotation-toolbar.inc' %]
68
69
                    [% IF existing_rotas.size > 0 %]
70
                        <h3>Manage existing rotas</h3>
71
                        <table class="rotas_table" role="grid">
72
                            <thead>
73
                                <th>Name</th>
74
                                <th>Cyclical</th>
75
                                <th>Active</th>
76
                                <th>Description</th>
77
                                <th>Number of items</th>
78
                                <th>&nbsp;</th>
79
                            </thead>
80
                            <tbody>
81
                                [% FOREACH rota IN existing_rotas %]
82
                                    <tr>
83
                                        <td>[% rota.title %]</td>
84
                                        <td>[% rota.cyclical ? 'Yes' : 'No'%]</td>
85
                                        <td>[% rota.active ? 'Yes' : 'No'%]</td>
86
                                        <td>[% rota.description %]</td>
87
                                        <td>[% rota.stockrotationitems.count %]</td>
88
                                        <td class="actions">
89
                                            <a class="btn btn-mini" href="?op=create_edit_rota&amp;rota_id=[% rota.rota_id %]">
90
                                                <i class="fa fa-pencil"></i>
91
                                                Edit
92
                                            </a>
93
                                            <div class="btn-group" role="group">
94
                                                <button type="button" class="btn btn-mini dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
95
                                                    Manage
96
                                                    <i class="fa fa-caret-down"></i>
97
                                                </button>
98
                                                <ul class="dropdown-menu">
99
                                                    <li><a href="?op=manage_stages&amp;rota_id=[% rota.rota_id %]">Stages</a></li>
100
                                                    [% IF CAN_user_stockrotation_can_add_items_rotas && rota.stockrotationstages.count > 0 %]
101
                                                    <li><a href="?op=manage_items&amp;rota_id=[% rota.rota_id %]">Items</a></li>
102
                                                    [% END %]
103
                                                </ul>
104
                                            </div>
105
                                            <a class="btn btn-mini" href="?op=toggle_rota&amp;rota_id=[% rota.rota_id %]">
106
                                                <i class="fa fa-power-off"></i>
107
                                                [% IF !rota.active %]
108
                                                    Activate
109
                                                [% ELSE %]
110
                                                    Deactivate
111
                                                [% END %]
112
                                            </a>
113
                                        </td>
114
                                    </tr>
115
                                [% END %]
116
                            </tbody>
117
                        </table>
118
                    [% END %]
119
120
                [% ELSIF (op == 'create_edit_rota') %]
121
122
                    [% IF rota.rota_id %]
123
                        <h3>Edit "[% rota.title %]"</h3>
124
                    [% ELSE %]
125
                        <h3>Create new rota</h3>
126
                    [% END %]
127
128
                    [% IF error == 'invalid_form' %]
129
                    <div class="dialog alert">
130
                        <h3>There was a problem with your form submission</h3>
131
                    </div>
132
                    [% END %]
133
134
                    <form id="rota_form" method="post" enctype="multipart/form-data" class="validated">
135
                        <fieldset class="rows">
136
                            <ol>
137
                                <li>
138
                                    <label class="required" for="title">Name:</label>
139
                                    <input type="text" id="title" name="title" value="[% rota.title %]" required="required" placeholder="Rota name">
140
                                    <span class="required">Required</span>
141
                                </li>
142
                                <li>
143
                                    <label for="cyclical">Cyclical:</label>
144
                                    <select name="cyclical" id="cyclical">
145
                                        [% IF rota.cyclical %]
146
                                            <option value="1" selected>Yes</option>
147
                                            <option value="0">No</option>
148
                                        [% ELSE %]
149
                                            <option value="1">Yes</option>
150
                                            <option value="0" selected>No</option>
151
                                        [% END %]
152
                                    </select>
153
                                </li>
154
                                <li>
155
                                    <label for="active">Active:</label>
156
                                    <select name="active" id="active">
157
                                        [% IF rota.active %]
158
                                            <option value="1" selected>Yes</option>
159
                                            <option value="0">No</option>
160
                                        [% ELSE %]
161
                                            <option value="1">Yes</option>
162
                                            <option value="0" selected>No</option>
163
                                        [% END %]
164
                                    </select>
165
                                </li>
166
                                <li>
167
                                    <label for="description">Description:</label>
168
                                    <textarea id="description" name="description" placeholder="Rota description">[% rota.description %]</textarea>
169
                                </li>
170
                            </ol>
171
                        </fieldset>
172
                        <fieldset class="action">
173
                            <input type="submit" value="Submit">
174
                            <a href="/cgi-bin/koha/tools/stockrotation.pl" class="cancel">Cancel</a>
175
                        </fieldset>
176
                        [% IF rota.rota_id %]
177
                            <input type="hidden" name="id" value="[% rota.rota_id %]">
178
                        [% END %]
179
                        <input type="hidden" name="op" value="process_rota">
180
                    </form>
181
182
                [% ELSIF (op == 'manage_stages') %]
183
184
                    [% INCLUDE 'stockrotation-toolbar.inc' %]
185
186
                    [% IF existing_stages.size > 0 %]
187
                        <div id="manage_stages">
188
                            <h3>Manage stages</h3>
189
                            <div id="ajax_status"
190
                                data-saving-msg="Saving changes..."
191
                                data-success-msg=""
192
                                data-failed-msg="Error: ">
193
                                <span id="ajax_saving_msg"></span>
194
                                <i id="ajax_saving_icon" class="fa fa-spinner fa-spin"></i>
195
                                <i id="ajax_success_icon" class="fa fa-check"></i>
196
                                <i id="ajax_failed_icon" class="fa fa-times"></i>
197
                                <span id="ajax_success_msg"></span>
198
                                <span id="ajax_failed_msg"></span>
199
                            </div>
200
                            <div id="manage_stages_help">
201
                                Stages can be re-ordered by using the <i class="drag_handle fa fa-lg fa-bars"></i>handle to drag and drop them to their new position
202
                            </div>
203
                            <div id="stage_list_headings">
204
                                <span class="stagename">Branch</span>
205
                                <span class="stageduration">Duration (days)</span>
206
                            </div>
207
                            <ul id="sortable_stages" data-rota-id="[% rota.rota_id %]">
208
                                [% FOREACH stage IN existing_stages %]
209
                                    <li id="stage_[% stage.stage_id %]">
210
                                        <span data-toggle="tooltip" title="Drag and drop to move this stage to another position" data-placement="right" class="stagename">
211
                                            [% IF existing_stages.size > 1 %]
212
                                                <i class="drag_handle fa fa-lg fa-bars"></i>
213
                                            [% END %]
214
                                            [% Branches.GetName(stage.branchcode_id) %]
215
                                        </span>
216
                                        <span class="stageduration">[% stage.duration %]</span>
217
                                        <span class="stageactions">
218
                                            <a class="btn btn-mini" href="?op=create_edit_stage&amp;stage_id=[% stage.stage_id %]">
219
                                                <i class="fa fa-pencil"></i> Edit
220
                                            </a>
221
                                            <a class="btn btn-mini" href="?op=confirm_delete_stage&amp;stage_id=[% stage.stage_id %]">
222
                                                <i class="fa fa-trash"></i> Delete
223
                                            </a>
224
                                        </span>
225
                                    </li>
226
                                [% END %]
227
                            </ul>
228
                        </div>
229
                    [% END %]
230
231
                    <p><a href="stockrotation.pl">Return to rotas</a></p>
232
233
                [% ELSIF (op == 'create_edit_stage') %]
234
235
                    [% IF stage.id %]
236
                        <h3>Edit "[% Branches.GetName(stage.branchcode_id) %]"</h3>
237
                    [% ELSE %]
238
                        <h3>Create new stage</h3>
239
                    [% END %]
240
241
                    [% IF error == 'invalid_form' %]
242
                    <div class="dialog alert">
243
                        <h3>There was a problem with your form submission</h3>
244
                    </div>
245
                    [% END %]
246
247
                    <form id="stage_form" method="post" enctype="multipart/form-data" class="validated">
248
                        <fieldset class="rows">
249
                            <ol>
250
                                <li>
251
                                    <label class="required" for="branch">Branch:</label>
252
                                    <select name="branchcode" id="branch">
253
                                        [% FOREACH branch IN branches %]
254
                                            [% IF branch.branchcode == stage.branchcode_id %]
255
                                                <option value="[% branch.branchcode %]" selected>[% Branches.GetName(branch.branchcode) %]</option>
256
                                            [% ELSE %]
257
                                                <option value="[% branch.branchcode %]">[% Branches.GetName(branch.branchcode) %]</option>
258
                                            [% END %]
259
                                        [% END %]
260
                                    </select>
261
                                    <span class="required">Required</span>
262
                                </li>
263
                                <li>
264
                                    <label class="required" for="duration">Duration:</label>
265
                                    <input type="text" id="duration" name="duration" value="[% stage.duration %]" required="required" placeholder="Duration (days)">
266
                                    <span class="required">Required</span>
267
                                </li>
268
                            </ol>
269
                        </fieldset>
270
                        <fieldset class="action">
271
                            <input type="submit" value="Submit">
272
                            <a href="/cgi-bin/koha/tools/stockrotation.pl?op=manage_stages&amp;rota_id=[% rota_id %]" class="cancel">Cancel</a>
273
                        </fieldset>
274
                        <input type="hidden" name="stage_id" value="[% stage.id %]">
275
                        <input type="hidden" name="rota_id" value="[% rota_id %]">
276
                        <input type="hidden" name="op" value="process_stage">
277
                    </form>
278
                [% ELSIF (op == 'confirm_remove_from_rota') %]
279
280
                    <div class="dialog alert">
281
                        <h3>Are you sure you wish to remove this item from it's rota</h3>
282
                        <p>
283
                            <a class="btn approve" href="?op=remove_item_from_stage&amp;item_id=[% item_id %]&amp;stage_id=[% stage_id %]&amp;rota_id=[% rota_id %]"><i class="fa fa-fw fa-check"></i>Yes</a>
284
                            <a class="btn deny" href="?op=manage_items&amp;rota_id=[% rota_id %]"><i class="fa fa-fw fa-remove"></i>No</a>
285
                        </p>
286
                    </div>
287
                [% ELSIF (op == 'confirm_delete_stage') %]
288
289
                    <div class="dialog alert">
290
                        <h3>Are you sure you want to delete this stage?</h3>
291
                        [% IF stage.stockrotationitems.count > 0 %]
292
                            <p>This stage contains the following item(s):</p>
293
                            <ul>
294
                                [% FOREACH item IN stage.stockrotationitems %]
295
                                    <li>[% item.itemnumber.biblio.title %] (Barcode: [% item.itemnumber.barcode %])</li>
296
                                [% END %]
297
                            </ul>
298
                        [% END %]
299
                        <p>
300
                            <a class="btn approve" href="?op=delete_stage&amp;stage_id=[% stage.stage_id %]"><i class="fa fa-fw fa-check"></i>Yes</a>
301
                            <a class="btn deny" href="?op=manage_stages&amp;rota_id=[% stage.rota.rota_id %]"><i class="fa fa-fw fa-remove"></i>No</a>
302
                        </p>
303
                    </div>
304
                [% ELSIF (op == 'manage_items') %]
305
306
                    [% INCLUDE 'stockrotation-toolbar.inc' %]
307
308
                    [% IF error %]
309
                        <div class="dialog alert">
310
                            [% IF error == "item_not_found" %]
311
                                <h3>The item was not found</h3>
312
                            [% ELSIF error == "already_on_rota" %]
313
                                <h3>This item is already on this rota</h3>
314
                            [% END %]
315
                        </div>
316
                    [% END %]
317
318
                    <div>
319
                        <form id="add_rota_item_form" method="post" enctype="multipart/form-data" class="validated">
320
                            <fieldset class="rows">
321
                                <legend>Add item to &quot;[% rota.title %]&quot;</legend>
322
                                <ol>
323
                                    <li>
324
                                        <label for="barcode">Barcode:</label>
325
                                        <input type="text" id="barcode" name="barcode" placeholder="Item barcode" autofocus>
326
                                    </li>
327
                                </ol>
328
                            </fieldset>
329
                            <fieldset class="rows">
330
                                <legend>Use a barcode file</legend>
331
                                <ol>
332
                                    <li>
333
                                        <label for="barcodefile">Barcode file:</label>
334
                                        <input type="file" id="barcodefile" name="barcodefile">
335
                                    </li>
336
                                </ol>
337
                            </fieldset>
338
                            <fieldset class="action">
339
                                <input type="submit" value="Submit">
340
                            </fieldset>
341
                            <input type="hidden" name="rota_id" value="[% rota.id %]">
342
                            <input type="hidden" name="op" value="add_items_to_rota">
343
                        </form>
344
                    </div>
345
346
                    [% IF items.count > 0 %]
347
                        <h3>Manage items assigned to &quot;[% rota.title %]&quot;</h3>
348
                        <table id="sr_manage_items" class="items_table" role="grid">
349
                            <thead>
350
                                <th>Barcode</th>
351
                                <th>Title</th>
352
                                <th>Author</th>
353
                                <th>Shelfmark</th>
354
                                <th class="NoSearch">In transit</th>
355
                                <th class="NoSort">Stages &amp; duration in days<br>(current stage highlighted)</th>
356
                                <th class="NoSort">&nbsp;</th>
357
                            </thead>
358
                            <tbody>
359
                                [% FOREACH item IN items %]
360
                                    <tr>
361
                                        <td><a href="/cgi-bin/koha/catalogue/moredetail.pl?itemnumber=[% item.id %]&amp;biblionumber=[% item.itemnumber.biblio.id %]#item[% item.id %]">[% item.itemnumber.barcode %]</a></td>
362
                                        <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% item.itemnumber.biblio.id %]">[% item.itemnumber.biblio.title %]</a></td>
363
                                        <td>[% item.itemnumber.biblio.author %]</td>
364
                                        <td>[% item.itemnumber.itemcallnumber %]</td>
365
                                        <td>[% item.itemnumber.get_transfer ? 'Yes' : 'No' %]</td>
366
                                        <td>
367
                                            [% FOREACH this_stage IN stages %]
368
                                                [% IF this_stage.stage_id == item.stage.stage_id %]
369
                                                    <span class="stage highlight_stage">
370
                                                [% ELSE %]
371
                                                    <span class="stage">
372
                                                [% END %]
373
                                                [% Branches.GetName(this_stage.branchcode_id) %] ([% this_stage.duration %])
374
                                                </span>
375
                                                &raquo;
376
                                            [% END %]
377
                                            [% IF stages.size > 0 %]
378
                                                <span class="stage">[% rota.cyclical ? 'START' : 'END' %]</span>
379
                                            [% END %]
380
                                        </td>
381
                                        <td class="actions">
382
                                            [% in_transit = item.itemnumber.get_transfer %]
383
                                            [% IF !in_transit && stages.size > 1 %]
384
                                                <a class="btn btn-mini" href="?op=move_to_next_stage&amp;rota_id=[% rota.id %]&amp;item_id=[% item.id %]&amp;stage_id=[% item.stage.stage_id %]">
385
                                            [% ELSE %]
386
                                                <a class="btn btn-mini" disabled>
387
                                            [% END %]
388
                                                <i class="fa fa-arrow-right"></i>
389
                                                Move to next stage
390
                                            </a>
391
                                            [% IF !in_transit %]
392
                                                <a class="btn btn-mini" href="?op=toggle_in_demand&amp;stage_id=[% item.stage.stage_id %]&amp;item_id=[% item.id %]&amp;rota_id=[% rota.id %]">
393
                                            [% ELSE %]
394
                                                <a class="btn btn-mini" disabled>
395
                                            [% END %]
396
                                                <i class="fa fa-fire"></i>
397
                                                [% item.indemand ? 'Remove &quot;In demand&quot;' : 'Add &quot;In demand&quot;' %]
398
                                            </a>
399
                                            [% IF !in_transit %]
400
                                                <a class="btn btn-mini" href="?op=confirm_remove_from_rota&amp;stage_id=[% item.stage.stage_id %]&amp;item_id=[% item.id %]&amp;rota_id=[% rota.id %]">
401
                                            [% ELSE %]
402
                                                <a class="btn btn-mini" disabled>
403
                                            [% END %]
404
                                                <i class="fa fa-trash"></i>
405
                                                Remove from rota
406
                                            </a>
407
                                        </td>
408
                                    </tr>
409
                                [% END %]
410
                            </tbody>
411
                        </table>
412
                    [% END %]
413
414
                    <p><a href="stockrotation.pl">Return to rotas</a></p>
415
416
                [% ELSIF op == 'add_items_to_rota' %]
417
418
419
                    <div class="dialog message">
420
                        <h3>Add items to rota report</h3>
421
                    </div>
422
                    <div>
423
                        [% IF barcode_status.ok.size > 0 %]
424
                            <h4>Items added to rota:</h4>
425
                            <ul>
426
                                [% FOREACH item_ok IN barcode_status.ok %]
427
                                    <li>[% item_ok.biblio.title %]</li>
428
                                [% END %]
429
                            </ul>
430
                        [% END %]
431
                        [% IF barcode_status.on_this.size > 0 %]
432
                            <h4>Items already on this rota:</h4>
433
                            <ul>
434
                                [% FOREACH item_on_this IN barcode_status.on_this %]
435
                                    <li>[% item_on_this.biblio.title %]</li>
436
                                [% END %]
437
                            </ul>
438
                        [% END %]
439
                        [% IF barcode_status.not_found.size > 0 %]
440
                            <h4>Barcodes not found:</h4>
441
                            <ul>
442
                                [% FOREACH barcode_not_found IN barcode_status.not_found %]
443
                                    <li>[% barcode_not_found %]</li>
444
                                [% END %]
445
                            </ul>
446
                        [% END %]
447
                        [% IF barcode_status.on_other.size > 0 %]
448
                            <h4>Items found on other rotas:</h4>
449
                            <ul>
450
                                [% FOREACH item_on_other IN barcode_status.on_other %]
451
                                    <li>[% item_on_other.biblio.title %]</li>
452
                                [% END %]
453
                            </ul>
454
                        [% END %]
455
                    </div>
456
                    [% IF barcode_status.on_other.size > 0 %]
457
                        <form id="add_rota_item_form" method="post" enctype="multipart/form-data">
458
                            <fieldset>
459
                                <legend>Select items to move to this rota:</legend>
460
                                [% FOREACH item_on_other IN barcode_status.on_other %]
461
                                    <li><input type="checkbox" name="move_item" value="[% item_on_other.itemnumber %]"> [% item_on_other.biblio.title %] (Currently on &quot;[% item_on_other.stockrotationitem.stage.rota.title %]&quot;)</li>
462
                                [% END %]
463
464
                            </fieldset>
465
                            <fieldset class="action">
466
                                <input type="submit" value="Submit">
467
                            </fieldset>
468
                            <input type="hidden" name="rota_id" value="[% rota_id %]">
469
                            <input type="hidden" name="op" value="move_items_to_rota">
470
                        </form>
471
                    [% END %]
472
                    <p><a href="?op=manage_items&amp;rota_id=[% rota_id %]">Return to rota</a></p>
473
474
                [% END %]
475
            </div>
476
        </div>
477
        <div class="yui-b">
478
            [% INCLUDE 'tools-menu.inc' %]
479
        </div>
480
    </div>
481
</div>
482
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/tools/stockrotation.pl (-1 / +519 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright 2016 PTFS-Europe Ltd
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
=head1 stockrotation.pl
21
22
 Script to handle stockrotation. Including rotas, their associated stages
23
 and items
24
25
=cut
26
27
use Modern::Perl;
28
use CGI;
29
30
use C4::Auth;
31
use C4::Output;
32
33
use Koha::Libraries;
34
use Koha::Stockrotationrotas;
35
use Koha::Stockrotationitems;
36
use Koha::Stockrotationstages;
37
use Koha::Item;
38
use Koha::Util::Stockrotation qw(:ALL);
39
40
my $input = new CGI;
41
42
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
43
    {
44
        template_name   => 'tools/stockrotation.tt',
45
        query           => $input,
46
        type            => 'intranet',
47
        authnotrequired => 0
48
    }
49
);
50
51
# Grab all passed data
52
# 'our' since Plack changes the scoping
53
# of 'my'
54
our %params = $input->Vars();
55
56
my $op = $params{op};
57
58
if (!defined $op) {
59
60
    # No operation is supplied, we're just displaying the list of rotas
61
    my $rotas = Koha::Stockrotationrotas->search->as_list;
62
63
    $template->param(
64
        existing_rotas => $rotas,
65
        no_op_set      => 1
66
    );
67
68
} elsif ($op eq 'create_edit_rota') {
69
70
    # Edit an existing rota or define a new one
71
    my $rota_id = $params{rota_id};
72
73
    my $rota = {};
74
75
    if (!defined $rota_id) {
76
77
        # No ID supplied, we're creating a new rota
78
        # Create a shell rota hashref
79
        $rota = {
80
            cyclical => 1,
81
            active   => 0
82
        };
83
84
    } else {
85
86
        # ID supplied, we're editing an existing rota
87
        $rota = Koha::Stockrotationrotas->find($rota_id);
88
89
    }
90
91
    $template->param(
92
        rota => $rota,
93
        op   => $op
94
    );
95
96
} elsif ($op eq 'toggle_rota') {
97
98
    # Find and update the active status of the rota
99
    my $rota = Koha::Stockrotationrotas->find($params{rota_id});
100
101
    my $new_active = ($rota->active == 1) ? 0 : 1;
102
103
    $rota->active($new_active)->store;
104
105
    # Return to rotas page
106
    print $input->redirect('stockrotation.pl');
107
108
} elsif ($op eq 'process_rota') {
109
110
    # Get a hashref of the submitted rota data
111
    my $rota = get_rota_from_form();
112
113
    if (!process_rota($rota)) {
114
115
        # The submitted rota was invalid
116
        $template->param(
117
            error => 'invalid_form',
118
            rota => $rota,
119
            op   => 'create_edit_rota'
120
        );
121
122
    } else {
123
124
        # All was well, return to the rotas list
125
        print $input->redirect('stockrotation.pl');
126
127
    }
128
129
} elsif ($op eq 'manage_stages') {
130
131
    my $rota = Koha::Stockrotationrotas->find($params{rota_id});
132
133
    $template->param(
134
        rota            => $rota,
135
        branches        => get_branches(),
136
        existing_stages => get_stages($rota),
137
        rota_id         => $params{rota_id},
138
        op              => $op
139
    );
140
141
} elsif ($op eq 'create_edit_stage') {
142
143
    # Edit an existing stage or define a new one
144
    my $stage_id = $params{stage_id};
145
146
    my $rota_id = $params{rota_id};
147
148
    if (!defined $stage_id) {
149
150
        # No ID supplied, we're creating a new stage
151
        $template->param(
152
            branches => get_branches(),
153
            stage    => {},
154
            rota_id  => $rota_id,
155
            op       => $op
156
        );
157
158
    } else {
159
160
        # ID supplied, we're editing an existing stage
161
        my $stage = Koha::Stockrotationstages->find($stage_id);
162
163
        $template->param(
164
            branches => get_branches(),
165
            stage    => $stage,
166
            rota_id  => $stage->rota->rota_id,
167
            op       => $op
168
        );
169
170
    }
171
172
} elsif ($op eq 'confirm_remove_from_rota') {
173
174
    # Get the stage we're deleting
175
    $template->param(
176
        op       => $op,
177
        rota_id  => $params{rota_id},
178
        stage_id => $params{stage_id},
179
        item_id  => $params{item_id}
180
    );
181
182
} elsif ($op eq 'confirm_delete_stage') {
183
184
    # Get the stage we're deleting
185
    my $stage = Koha::Stockrotationstages->find($params{stage_id});
186
187
    $template->param(
188
        op    => $op,
189
        stage => $stage
190
    );
191
192
} elsif ($op eq 'delete_stage') {
193
194
    # Get the stage we're deleting
195
    my $stage = Koha::Stockrotationstages->find($params{stage_id});
196
197
    # Get the ID of the rota with which this stage is associated
198
    # (so we can return to the "Manage stages" page after deletion)
199
    my $rota_id = $stage->rota->rota_id;
200
201
    $stage->delete;
202
203
    # Return to the stages list
204
    print $input->redirect("?op=manage_stages&rota_id=$rota_id");
205
206
} elsif ($op eq 'process_stage') {
207
208
    # Get a hashref of the submitted stage data
209
    my $stage = get_stage_from_form();
210
211
    # The rota we're managing
212
    my $rota_id = $params{rota_id};
213
214
    if (!process_stage($stage, $rota_id)) {
215
216
        # The submitted stage was invalid
217
        # Get all branches
218
        my $branches = get_branches();
219
220
        $template->param(
221
            error        => 'invalid_form',
222
            all_branches => $branches,
223
            stage        => $stage,
224
            rota_id      => $rota_id,
225
            op           => 'create_edit_stage'
226
        );
227
228
    } else {
229
230
        # All was well, return to the stages list
231
        print $input->redirect("?op=manage_stages&rota_id=$rota_id");
232
233
    }
234
235
} elsif ($op eq 'manage_items') {
236
237
    my $rota = Koha::Stockrotationrotas->find($params{rota_id});
238
239
    # Get all items on this rota, for each prefetch their
240
    # stage and biblio objects
241
    my $items = Koha::Stockrotationitems->search(
242
        { 'stage.rota_id' => $params{rota_id} },
243
        {
244
            prefetch => {
245
                stage => {
246
                    'stockrotationitems' => {
247
                        'itemnumber' => 'biblionumber'
248
                    }
249
                }
250
            }
251
        }
252
    );
253
254
    $template->param(
255
        rota_id  => $params{rota_id},
256
        error    => $params{error},
257
        items    => $items,
258
        branches => get_branches(),
259
        stages   => get_stages($rota),
260
        rota     => $rota,
261
        op       => $op
262
    );
263
264
} elsif ($op eq 'move_to_next_stage') {
265
266
    move_to_next_stage($params{item_id}, $params{stage_id});
267
268
    # Return to the items list
269
    print $input->redirect("?op=manage_items&rota_id=" . $params{rota_id});
270
271
} elsif ($op eq 'toggle_in_demand') {
272
273
    # Toggle the item's in_demand
274
    toggle_indemand($params{item_id}, $params{stage_id});
275
276
    # Return to the items list
277
    print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
278
279
} elsif ($op eq 'remove_item_from_stage') {
280
281
    # Remove the item from the stage
282
    remove_from_stage($params{item_id}, $params{stage_id});
283
284
    # Return to the items list
285
    print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
286
287
} elsif ($op eq 'add_items_to_rota') {
288
289
    # The item's barcode,
290
    # which we may or may not have been passed
291
    my $barcode = $params{barcode};
292
293
    # The rota we're adding the item to
294
    my $rota_id = $params{rota_id};
295
296
    # The uploaded file filehandle,
297
    # which we may or may not have been passed
298
    my $barcode_file = $input->upload("barcodefile");
299
300
    # We need to create an array of one or more barcodes to
301
    # insert
302
    my @barcodes = ();
303
304
    # If the barcode input box was populated, use it
305
    push @barcodes, $barcode if $barcode;
306
307
    # Only parse the uploaded file if necessary
308
    if ($barcode_file) {
309
310
        # Call binmode on the filehandle as we want to set a
311
        # UTF-8 layer on it
312
        binmode($barcode_file, ":encoding(UTF-8)");
313
        # Parse the file into an array of barcodes
314
        while (my $barcode = <$barcode_file>) {
315
            $barcode =~ s/\r/\n/g;
316
            $barcode =~ s/\n+/\n/g;
317
            my @data = split(/\n/, $barcode);
318
            push @barcodes, @data;
319
        }
320
321
    }
322
323
    # A hashref to hold the status of each barcode
324
    my $barcode_status = {
325
        ok        => [],
326
        on_other  => [],
327
        on_this   => [],
328
        not_found => []
329
    };
330
331
    # If we have something to work with, do it
332
    get_barcodes_status($rota_id, \@barcodes, $barcode_status) if (@barcodes);
333
334
    # Now we know the status of each barcode, add those that
335
    # need it
336
    if (scalar @{$barcode_status->{ok}} > 0) {
337
338
        add_items_to_rota($rota_id, $barcode_status->{ok});
339
340
    }
341
    # If we were only passed one barcode and it was successfully
342
    # added, redirect back to ourselves, we don't want to display
343
    # a report, redirect also if we were passed no barcodes
344
    if (
345
        scalar @barcodes == 0 ||
346
        (scalar @barcodes == 1 && scalar @{$barcode_status->{ok}} == 1)
347
    ) {
348
349
        print $input->redirect("?op=manage_items&rota_id=$rota_id");
350
351
    } else {
352
353
        # Report on the outcome
354
        $template->param(
355
            barcode_status => $barcode_status,
356
            rota_id        => $rota_id,
357
            op             => $op
358
        );
359
360
    }
361
362
} elsif ($op eq 'move_items_to_rota') {
363
364
    # The barcodes of the items we're moving
365
    my @move = $input->param('move_item');
366
367
    foreach my $item(@move) {
368
369
        # The item we're moving
370
        my $item = Koha::Items->find($item);
371
372
        # Move it to the new rota
373
        $item->add_to_rota($params{rota_id});
374
375
    }
376
377
    # Return to the items list
378
    print $input->redirect("?op=manage_items&rota_id=".$params{rota_id});
379
380
}
381
382
output_html_with_http_headers $input, $cookie, $template->output;
383
384
sub get_rota_from_form {
385
386
    return {
387
        id          => $params{id},
388
        title       => $params{title},
389
        cyclical    => $params{cyclical},
390
        active      => $params{active},
391
        description => $params{description}
392
    };
393
}
394
395
sub get_stage_from_form {
396
397
    return {
398
        stage_id    => $params{stage_id},
399
        branchcode  => $params{branchcode},
400
        duration    => $params{duration}
401
    };
402
}
403
404
sub process_rota {
405
406
    my $sub_rota = shift;
407
408
    # Fields we require
409
    my @required = ('title','cyclical','active');
410
411
    # Count of the number of required fields we have
412
    my $valid = 0;
413
414
    # Ensure we have everything we require
415
    foreach my $req(@required) {
416
417
        if (exists $sub_rota->{$req}) {
418
419
            chomp(my $value = $sub_rota->{$req});
420
            if (length $value > 0) {
421
                $valid++;
422
            }
423
424
        }
425
426
    }
427
428
    # If we don't have everything we need
429
    return 0 if $valid != scalar @required;
430
431
    # Passed validation
432
    # Find the rota we're updating
433
    my $rota = Koha::Stockrotationrotas->find($sub_rota->{id});
434
435
    if ($rota) {
436
437
        $rota->title(
438
            $sub_rota->{title}
439
        )->cyclical(
440
            $sub_rota->{cyclical}
441
        )->active(
442
            $sub_rota->{active}
443
        )->description(
444
            $sub_rota->{description}
445
        )->store;
446
447
    } else {
448
449
        $rota = Koha::Stockrotationrota->new({
450
            title       => $sub_rota->{title},
451
            cyclical    => $sub_rota->{cyclical},
452
            active      => $sub_rota->{active},
453
            description => $sub_rota->{description}
454
        })->store;
455
456
    }
457
458
    return 1;
459
}
460
461
sub process_stage {
462
463
    my ($sub_stage, $rota_id) = @_;
464
465
    # Fields we require
466
    my @required = ('branchcode','duration');
467
468
    # Count of the number of required fields we have
469
    my $valid = 0;
470
471
    # Ensure we have everything we require
472
    foreach my $req(@required) {
473
474
        if (exists $sub_stage->{$req}) {
475
476
            chomp(my $value = $sub_stage->{$req});
477
            if (length $value > 0) {
478
                $valid++;
479
            }
480
481
        }
482
483
    }
484
485
    # If we don't have everything we need
486
    return 0 if $valid != scalar @required;
487
488
    # Passed validation
489
    # Find the stage we're updating
490
    my $stage = Koha::Stockrotationstages->find($sub_stage->{stage_id});
491
492
    if ($stage) {
493
494
        # Updating an existing stage
495
        $stage->branchcode_id(
496
            $sub_stage->{branchcode}
497
        )->duration(
498
            $sub_stage->{duration}
499
        )->store;
500
501
    } else {
502
503
        # Creating a new stage
504
        $stage = Koha::Stockrotationstage->new({
505
            branchcode_id  => $sub_stage->{branchcode},
506
            rota_id        => $rota_id,
507
            duration       => $sub_stage->{duration}
508
        })->store;
509
510
    }
511
512
    return 1;
513
}
514
515
=head1 AUTHOR
516
517
Andrew Isherwood <andrew.isherwood@ptfs-europe.com>
518
519
=cut

Return to bug 11897