Subversion Repositories OpenARM Single-board Computer

Rev

Rev 333 | Rev 335 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
280 agaran 1
#!/usr/bin/perl -w
2
use strict;
293 agaran 3
# $Id: inventory.pl 334 2008-12-29 21:36:34Z agaran $
280 agaran 4
# Thu, 13 Nov 2008 21:06:23 +0100
5
# Maciej 'agaran' Pijanka <agaran@pld-linux.org>
6
# for OpenARM SBC Project
7
# license: gpl v3
8
 
293 agaran 9
 
280 agaran 10
use Getopt::Long qw//;
293 agaran 11
use File::Basename qw/basename/;
322 agaran 12
use IO::Dir;
13
use IO::File;
280 agaran 14
 
325 agaran 15
use lib File::Basename::dirname($0).'/lib';
326 agaran 16
use Text::Table;
325 agaran 17
 
18
 
313 agaran 19
my %Config;
280 agaran 20
 
313 agaran 21
# ==================================================
22
$Config{docdir} = '.';
23
$Config{bomdir} = '.';
24
$Config{verbose} = 1;
280 agaran 25
 
313 agaran 26
# 0 mean not show, -1 show all, positive value limits depth of shown
27
$Config{dbg_showdirs} = 0;
280 agaran 28
 
313 agaran 29
 
30
# modes
31
my $build_inventory = 0;
32
my $build_bom = 0;
33
my $show_conf = 0;
34
my $show_help = 0;
35
 
36
# ==================================================
37
 
38
my %Inv_By_PartNo;
39
my @Inv;
325 agaran 40
my %BomData;
332 agaran 41
my %Files;
313 agaran 42
 
293 agaran 43
sub err_printf($@) {
44
        my ($format, @args) = @_;
280 agaran 45
 
313 agaran 46
        printf STDERR "-E- ".$format."\n", @args;
47
        # exit? or fail-exit here
280 agaran 48
}
49
 
293 agaran 50
sub wrn_printf($@) {
51
        my ($format, @args) = @_;
328 agaran 52
        return if ($Config{verbose} <= 1) ;
293 agaran 53
        printf STDERR "-W- ".$format."\n", @args;
54
}
55
 
56
sub inf_printf($@) {
57
        my ($format, @args) = @_;
328 agaran 58
        return if ($Config{verbose} <= 2) ;
293 agaran 59
        printf STDERR "-I- ".$format."\n", @args;
60
}
61
 
313 agaran 62
sub Config_Show {
63
        printf "Config for %s\n----------------------------------------\n", basename($0);
64
        foreach my $name (sort keys %Config) {
65
                printf "%-20s %s\n", $name, $Config{$name};
66
        }
67
}
68
 
69
sub Help_Show {
70
        printf "Help for %s\n----------------------------------------\n", basename($0);
71
        unless (defined($_[1]) && length($_[1]) != 0) {
72
                print "Basic help\n\t--showrc|showconf shows current configuration\n".
73
                        "\t--docdir|-d <dir> tells script where information.txt files should be searched\n".
74
                        "\t--define <something>=<somethingelseornot> defines some configration value\n".
319 agaran 75
                        "\t--verbose|-v [level] sets verbosity level\nOrder of options DOES matter\n".
330 agaran 76
                        "\t--bomdir|-b <dir> tells script where boms should be searched\n".
331 agaran 77
                        "\t--outfile|-o <file> tells where script shall save output data\n".
330 agaran 78
                        "\t--force|-f forces script to save data even if file exist already\n";
319 agaran 79
 
80
                print "\t\e[0;31mThis Help Is created especially for \e[0;33mJelle\e[0;29m\n".
81
                        "\tThis script search all directories below specified ones to find interesting files\n";
313 agaran 82
                return;
83
        }
84
        if ($_[1] =~ /^foo$/) {
85
                print "Noo, there is no foo's here\n";
86
        } else {
87
                printf "Sorry, help for `%s' don't exist (eventually) yet\n", $_[1];
88
        }
89
        exit;
90
}
91
 
92
sub fix_dir ($) {
93
        my $dir = shift @_;
94
 
95
        $dir =~ s/\/$//;
96
 
97
        if (! -d $dir) {
98
                err_printf("Sorry `%s' is not valid directory, exiting", $dir);
99
        }
100
 
101
        return $dir;
102
}
103
 
104
sub shortdir ($) {
105
        my $path = shift @_;
106
 
107
        return substr($path, 2) if ($path =~ /^\.\//) ;
108
        return $path;
109
}
110
 
111
 
112
sub trim($) {
113
        my ($value) = @_;
114
 
115
        $value =~ s/^ +//;
116
        $value =~ s/ +$//;
117
        return $value;
118
}
119
 
120
sub etrim($) {
121
        my ($value) = @_;
122
 
123
        $value =~ s/^[  ]+//;
124
        $value =~ s/[   ]+$//;
125
        return $value;
126
}
127
 
330 agaran 128
sub strbreak($$) {
325 agaran 129
        my ($str,$lim) = @_;
334 agaran 130
        my @p = split /\ /,$str;
330 agaran 131
        $str = '';
132
        my $l = 0;
133
        while (@p) {
134
                my $e = shift @p;
135
                if ($l + length ($e) +1 > $lim) {
136
                        $str .= "\n".$e;
137
                        $l = length $e;
138
                } else {
139
                        $str .= " ".$e;
140
                        $l += length($e) + 1;
141
                }
142
        }
325 agaran 143
        return $str;
144
}
145
 
313 agaran 146
# this subroutine is used as callback function
147
# executed by file_lookup
280 agaran 148
sub parse_ifile($) {
293 agaran 149
        my ($filepath) = @_;
280 agaran 150
 
318 agaran 151
 
293 agaran 152
        open(IN, $filepath) or return 1;
280 agaran 153
 
293 agaran 154
        my %data;
280 agaran 155
 
293 agaran 156
        while (not eof IN) {
157
                my $line = <IN>;
280 agaran 158
 
293 agaran 159
                chomp $line;
280 agaran 160
 
293 agaran 161
                next if ($line =~ /^[   ]*$/);
162
                next if ($line =~ /^;/);
163
 
164
                last if ($line =~ /^--/);
165
 
166
                if ($line =~ /^([A-Za-z ]+):(.*)$/) {
313 agaran 167
                        my ($name,$value) = (lc etrim($1),etrim($2));
293 agaran 168
 
169
                        if ($name =~ /^price$/) {
170
                                $value =~ s/[^0-9\.\,]//g;
171
                                if ($value =~ s/^([0-9]+)[,.]([0-9]+)$/$1.$2/) {
172
                                        # printf STDERR "Price %.3f\n", $value;
173
                                        if (!defined($data{price})) {
174
                                                $data{price} = $value;
175
                                        } else {
313 agaran 176
                                                wrn_printf("Duplicated price field in file %s", shortdir($filepath));
293 agaran 177
                                        }
178
                                } else {
313 agaran 179
                                        err_printf("Bad price field in file %s", shortdir($filepath));
293 agaran 180
                                }
181
                        } elsif ($name =~ /^manufacturer$/i) {
182
                                # printf STDERR "Manufacturer %s\n", $value;
183
                                if (!defined($data{manufacturer})) {
184
                                        $data{manufacturer} = $value;
185
                                } else {
313 agaran 186
                                        wrn_printf("Duplicated manufacturer field in file %s", shortdir($filepath));
293 agaran 187
                                }
188
                        } elsif ($name =~ /^manufacturer part no$/ ) {
189
                                # printf STDERR "ManPartNo %s\n", $value;
190
                                if (!defined($data{manufact_partno})) {
313 agaran 191
                                        $data{manufact_partno} = trim($value);
293 agaran 192
                                } else {
193
                                        wrn_printf("Duplicated manufacturer part no field in file %s",
313 agaran 194
                                                shortdir($filepath));
293 agaran 195
                                }
196
                        } elsif ($name =~ /^description$/i) {
313 agaran 197
                                if (!defined($data{desc})) {
198
                                        $data{desc} = $value;
199
                                } else {
200
                                        wrn_printf("Duplicated description no field in file %s",
201
                                                shortdir($filepath));
202
                                }
293 agaran 203
                        } elsif ($name =~ /^datasheet$/i) {
204
                                $data{datasheet} = [] unless defined $data{datasheet};
205
                                push @{$data{datasheet}}, $value;
206
                                # printf STDERR "Datasheet %s\n", $value;
207
                        } elsif ($name =~ /^supplier$/i) {
208
                                # printf STDERR "Supplier %s\n", $value;
209
                                if (!defined($data{supplier})) {
210
                                        $data{supplier} = $value;
211
                                } else {
212
                                        wrn_printf("Duplicated supplier field in file %s",
313 agaran 213
                                                shortdir($filepath));
293 agaran 214
                                }
215
                        } elsif ($name =~ /^order code$/) {
216
                                # printf STDERR "Order Code %s\n", $value;
217
                                if (!defined($data{ordercode})) {
218
                                        $data{ordercode} = $value;
219
                                } else {
220
                                        wrn_printf("Duplicated order code field in file %s",
313 agaran 221
                                                shortdir($filepath));
293 agaran 222
                                }
223
                                #push @DATA, { $name => $value };
224
                        } elsif ($name =~ /^url .*$/) {
225
                                # printf STDERR "URL %s\n", $value;
226
                        } elsif ($name =~ /^catalog(ue|) page$/) {
227
                                # printf STDERR "Catalogue Page %s\n", $value;
228
                        } else {
229
                                err_printf("Unhandled field %s in file %s", $name,
313 agaran 230
                                        shortdir($filepath));
293 agaran 231
                        }
232
 
233
                } else {
328 agaran 234
                        wrn_printf("Unparseable line `%s', forgot ; to set it as comment in file %s", $line,
313 agaran 235
                                shortdir($filepath));
293 agaran 236
                }
237
        }
238
        close(IN);
239
 
240
        if (scalar keys %data == 0) {
241
                inf_printf("Skipping file %s because contain no data for me",
313 agaran 242
                        shortdir($filepath));
293 agaran 243
                return;
244
        }
245
 
246
        unless (defined ($data{price}) && $data{price} != 0) {
247
                wrn_printf("Missing Price in file %s",
313 agaran 248
                        shortdir($filepath));
293 agaran 249
        }
250
 
251
        unless (defined($data{manufact_partno})) {
252
                inf_printf("Missing Manufacturer Part No in file %s",
313 agaran 253
                        shortdir($filepath));
293 agaran 254
        }
255
 
256
        unless (defined($data{ordercode})) {
257
                wrn_printf("Missing Order Code in file %s",
313 agaran 258
                        shortdir($filepath));
293 agaran 259
        }
260
 
313 agaran 261
 
293 agaran 262
        use Data::Dumper qw/Dumper/;
313 agaran 263
 
264
        my $id = scalar @Inv;
265
 
266
        $Inv[$id] = {};
267
 
268
        $Inv[$id]{Datasheet} = delete $data{'datasheet'} if defined $data{'datasheet'};
334 agaran 269
        $Inv[$id]{Manufacturer} = etrim(trim(delete $data{'manufacturer'})) if defined $data{'manufacturer'};
270
        $Inv[$id]{Description} = etrim(trim(delete $data{'desc'})) if defined $data{'desc'};
313 agaran 271
        $Inv[$id]{Price} = delete $data{'price'} if defined $data{'price'};
334 agaran 272
        $Inv[$id]{Ordercode} = etrim(trim(delete $data{'ordercode'})) if defined $data{'ordercode'};
273
        $Inv[$id]{Manufacturer_Partno} = etrim(trim(delete $data{'manufact_partno'})) if defined $data{'manufact_partno'};
313 agaran 274
        $Inv[$id]{Supplier} = delete $data{'supplier'} if defined $data{'supplier'};
275
 
276
        unless (defined ($Inv_By_PartNo{$Inv[$id]{Manufacturer_Partno}})) {
277
                $Inv_By_PartNo{$Inv[$id]{Manufacturer_Partno}} = $id;
278
        } else {
279
                wrn_printf("PartNumber %s happened more than once, using first occurence (id:%d)",
280
                        $Inv[$id]{Manufacturer_Partno}, $id);
281
        }
325 agaran 282
 
313 agaran 283
        #inf_printf("Part %s defined in file %s", $Inv[$id]{Manufacturer_Partno}, shortdir($filepath));
293 agaran 284
 
313 agaran 285
        wrn_printf("Unhandled data from parsing: %s", Dumper(\%data)) if (scalar keys %data > 0);
280 agaran 286
}
287
 
288
 
313 agaran 289
sub parse_bom ($) {
290
        my ($filepath) = @_;
280 agaran 291
 
313 agaran 292
        open(IN, $filepath) or return 1;
332 agaran 293
        my $fkey = substr(basename($filepath),0,length(basename($filepath))-4);
313 agaran 294
 
295
        #wrn_printf("GotARg: %s", shortdir($filepath));
296
 
297
        my @Fields;
298
 
299
        my %data;
300
        my $v = '';
301
        while (not eof IN) {
302
                my $line = <IN>;
303
 
304
                chomp $line;
305
 
306
                if ($line =~ /^\.START$/) {
307
                        $v = 'boms';
308
                        next;
309
                }
310
 
311
                if ($line =~ /^\.END$/) {
312
                        $v = '';
313
                        next;
314
                }
315
 
316
                if ($v eq 'boms') {
317
                        @Fields = split(/\t/, substr($line,2));
318
                        $v = 'bom';
319
                        # some funny way to generate field-map
320
                        # that if someone reorder bom file columns we are still on place
321
                        next;
322
                }
323
 
324
                if ($v eq 'bom') {
325
                        my ($refdes, $device, $value, $footprint, $quantity) = split (/\t/, $line);
326
 
327
                        $device = trim($device);
328
 
329
#                       wrn_printf("Device: %s (value: %s) at refdes %s, %d pcs", $device, $value, $refdes, $quantity);
330
 
331
                        if (!defined $Inv_By_PartNo{$device}) {
332
                                wrn_printf("Device %s not found in inventory in file %s", $device, shortdir($filepath));
333
                                next;
334
                        }
335
                        my $id = $Inv_By_PartNo{$device};
336
                        next if ($Inv[$id]{Manufacturer} =~ /none/i); # skip parts whose manufacturer is none
337
                        #inf_printf("Found in Inventory at %d %s", $id, $Inv[$id]{Manufacturer_Partno});
334 agaran 338
                        push @{$data{$id}{RefDes}}, etrim(trim($refdes));
330 agaran 339
                        if (!defined $data{$id}{Footprint}) {
340
                                $data{$id}{Footprint} = $footprint;
341
                        } else {
342
                                if ($data{$id}{Footprint} ne $footprint) {
343
                                        err_printf("Different footprints for same device in within single bom file, script ".
344
                                                "cannot work around this, will use first one, but expect that output file ".
345
                                                "might have errors.");
346
                                }
347
                        }
313 agaran 348
                }
349
        }
350
        close(IN);
351
 
352
        if (scalar keys %data == 0) {
353
                inf_printf("Skipping file %s because contain no data for me",
354
                        shortdir($filepath));
355
                return;
356
        }
357
 
358
        foreach my $id (keys %data) {
359
                my %tmp;
360
                map { $tmp{$_} = 1 } @{$data{$id}{RefDes}};
361
                @{$data{$id}{RefDes}} = keys %tmp;
362
                my $cnt = scalar @{$data{$id}{RefDes}};
363
                if (!defined $Inv[$id]{Price}) {
364
                        wrn_printf("%s has no price, setting to 0.0", $Inv[$id]{Manufacturer_Partno});
365
                        $Inv[$id]{Price} = 0;
366
                }
318 agaran 367
                push @{$BomData{$id}{RefDes}}, @{$data{$id}{RefDes}};
330 agaran 368
                if (!defined $BomData{$id}{Footprint}) {
369
                        $BomData{$id}{Footprint} = $data{$id}{Footprint};
370
                } else {
371
                        if ($data{$id}{Footprint} ne $data{$id}{Footprint}) {
372
                                err_printf("Different footprints for same device between sheets, script cannot ".
373
                                        "work around this, will use first one, but expect that output file might ".
374
                                        "have errors.");
375
                        }
376
                }
332 agaran 377
                push @{$BomData{$id}{Files}}, $fkey;
378
                push @{$Files{$fkey}{$id}{RefDes}}, @{$data{$id}{RefDes}};
318 agaran 379
 
313 agaran 380
#               printf "%-20s %.4f %s\n", $Inv[$id]{Manufacturer_Partno}, $cnt, $icost, join (', ', @{$data{$id}{RefDes}});
381
#               %BomData{ById}{$id}{RefDes}
382
        }
383
}
384
 
385
sub file_lookup ($$$$) ;
386
sub file_lookup ($$$$) {
387
        my ($dir, $depth, $regexp, $callback) = @_;
388
 
389
        err_printf("Sorry, callback must be CODE ref") unless (ref $callback eq 'CODE');
390
 
293 agaran 391
        if ( -d $dir) {
322 agaran 392
                my $d = IO::Dir->new($dir);
393
                return 1 if (!defined $d);
394
 
395
 
396
                while (defined(my $e = $d->read)) {
293 agaran 397
                        my $fe = $dir .'/'. $e;
398
                        if ( -f $fe) {
313 agaran 399
                                if ($fe =~ $regexp) {
400
                                        &$callback($fe);
293 agaran 401
                                }
402
                        } elsif (-d $fe) { # now its dir...
403
                                if ($e eq '.svn') { # if entry name is equal to svn
404
                                        next; # go to next entry in foreach loop
405
                                }
406
                                next if ($e eq '.' or $e eq '..'); # skip to next if dir entry is . or ..
281 jelle 407
 
313 agaran 408
                                if ($Config{dbg_showdirs} == -1 or $Config{dbg_showdirs} > $depth) {
409
                                        printf STDERR "Entering directory %s\n", shortdir($fe);
293 agaran 410
                                }
313 agaran 411
                                return 1 if (file_lookup($fe, $depth+1, $regexp, $callback) == 1);
293 agaran 412
                        } else {
313 agaran 413
                                # symlink or other mysterius beast
293 agaran 414
                        }
415
                }
416
        }
313 agaran 417
        return 0;
280 agaran 418
}
419
 
293 agaran 420
Getopt::Long::Configure("bundling");
280 agaran 421
 
319 agaran 422
if (scalar @ARGV == 0) {
423
        Help_Show();
424
        exit;
425
}
426
 
313 agaran 427
my $result = Getopt::Long::GetOptions (
428
        "showrc|showconf" => sub { $show_conf = 1 },
429
        "docdir|d=s" => sub { $Config{docdir} = $_[1]; },
430
        # not sure if bomdir or SCH dir
431
        "bomdir|b=s" => sub { $Config{$_[0]} = $_[1]; },  # $_[0] contain basename of option, so in few cases could be (ab)used
432
        "define|D=s" => sub { my ($p,$q) = split(/=/,$_[1],2); $Config{$p} = $q; },
433
        "verbose|v:+" => sub { $Config{$_[0]} = ($_[1]>1?0:$Config{$_[0]}) + $_[1]; },
330 agaran 434
        "help|h|?:s" => sub { $show_help = 1; },
435
        "outfile|o=s" => sub { $Config{$_[0]} = $_[1]; },
436
        "force|f" => sub { $Config{$_[0]} = 1 },
313 agaran 437
 
438
        # options 
293 agaran 439
);
440
if (!$result) {
441
        printf "Usage: %s [-d directory] [-v]\n",basename($0);
442
        exit;
443
}
313 agaran 444
 
445
# ==================================================
446
# processing of options/config values, checking ranges etc
447
# 
448
 
449
$Config{docdir} = fix_dir ($Config{docdir});
450
$Config{bomdir} = fix_dir ($Config{bomdir});
451
 
452
if ( $show_help == 1) {
453
        Help_Show();
454
        exit;
293 agaran 455
}
456
 
313 agaran 457
if ( $show_conf == 1) {
458
        Config_Show();
459
        exit;
460
}
293 agaran 461
 
313 agaran 462
# make Inventory
328 agaran 463
printf STDERR "Indexing information.txt (under %s)\n",shortdir($Config{docdir});
313 agaran 464
file_lookup($Config{docdir}, 0, qr/\/information.txt$/, \&parse_ifile);
328 agaran 465
printf STDERR "\tFinished, %d entries loaded\n", scalar(@Inv)+1;
313 agaran 466
 
467
# process BOM files
328 agaran 468
printf STDERR "Loading bom data from %s\n", shortdir($Config{bomdir});
313 agaran 469
file_lookup($Config{bomdir}, 0, qr/\.bom$/, \&parse_bom);
328 agaran 470
printf STDERR "\tLoaded, now processing\n";
313 agaran 471
 
330 agaran 472
my ($bn,$pn) = (1,1);
318 agaran 473
my $cost = 0.0;
474
 
330 agaran 475
my $out;
476
if (!defined $Config{outfile}) {
477
        $Config{outfile} = './output.txt';
478
        wrn_printf("Output file not specified, saving out in ".$Config{outfile});
479
}
318 agaran 480
 
330 agaran 481
if ( -e $Config{outfile}) {
482
        unless (defined $Config{force} && $Config{force} == 1) {
483
                inf_printf("Unlinking output.txt before owrewriting");
484
                unlink($Config{outfile});
485
        } else {
486
                wrn_printf("Output file already exist, add --force if i shall overwrite it");
487
                exit;
488
        }
489
}
318 agaran 490
 
330 agaran 491
$out = new IO::File $Config{outfile}, 'w';
318 agaran 492
 
325 agaran 493
my $bomtable = Text::Table->new(
327 agaran 494
        { title => '| ', is_sep => 1 },
329 jelle 495
        { title => 'id', align => 'right', align_title => 'left' },
327 agaran 496
        { title => ' | ', is_sep => 1 },
329 jelle 497
        { title => 'description', align => 'left', align_title => 'left' },
327 agaran 498
        { title => ' | ', is_sep => 1 },
329 jelle 499
        { title => 'manufacturer partid', align => 'left', align_title => 'left' },
327 agaran 500
        { title => ' | ', is_sep => 1 },
329 jelle 501
        { title => 'manufacturer', align => 'left', align_title => 'left' },
327 agaran 502
        { title => ' | ', is_sep => 1 },
329 jelle 503
        { title => 'order code', align => 'left', align_title => 'left' },
327 agaran 504
        { title => ' | ', is_sep => 1 },
329 jelle 505
        { title => 'quantity', align => 'right', align_title => 'left' },
327 agaran 506
        { title => ' | ', is_sep => 1 },
329 jelle 507
        { title => "price", align => 'right', align_title => 'left' },
327 agaran 508
        { title => ' | ', is_sep => 1 },
329 jelle 509
        { title => "cost", align => 'right', align_title => 'left' },
327 agaran 510
        { title => ' |', is_sep => 1 },
325 agaran 511
);
330 agaran 512
 
513
my $parttable = Text::Table->new(
514
        { title => '| ', is_sep => 1 },
515
        { title => 'id', align => 'right', align_title => 'center' },
516
        { title => ' | ', is_sep => 1 },
517
        { title => 'description', align => 'left', align_title => 'center' },
518
        { title => ' | ', is_sep => 1 },
519
        { title => 'manufacturer partno', align => 'left', align_title => 'center' },
520
        { title => ' | ', is_sep => 1 },
521
        { title => 'footprint', align => 'left', align_title => 'center' },
522
        { title => ' | ', is_sep => 1 },
523
        { title => 'refdes', align => 'left', align_title => 'center' },
524
        { title => ' | ', is_sep => 1 },
525
        { title => 'sheet', align => 'left', align_title => 'center' },
526
        { title => ' |', is_sep => 1 },
527
);
325 agaran 528
 
529
foreach my $id ( sort {my $p = $Inv[$a]{Manufacturer} cmp $Inv[$b]{Manufacturer}; if ($p == 0) {
530
#               return $Inv[$a]{Manufacturer_Partno} cmp $Inv[$b]{Manufacturer_Partno}}; return $p; }  keys %BomData) {
531
                return $Inv[$a]{Description} cmp $Inv[$b]{Description}}; return $p; }  keys %BomData) {
318 agaran 532
        my %tmp;
533
        map { $tmp{$_} = 1 } @{$BomData{$id}{RefDes}};
534
        @{$BomData{$id}{RefDes}} = keys %tmp;
325 agaran 535
        my $quant = scalar @{$BomData{$id}{RefDes}};
318 agaran 536
        if (!defined $Inv[$id]{Price}) {
537
                wrn_printf("%s has no price, setting to 0.0", $Inv[$id]{Manufacturer_Partno});
538
                $Inv[$id]{Price} = 0;
539
        }
325 agaran 540
        my $icost = $quant * $Inv[$id]{Price};
318 agaran 541
 
330 agaran 542
#       printf PARTMAP "%-35s|%s\n", shortstring($Inv[$id]{Description},35), join (', ', sort @{$BomData{$id}{RefDes}});
318 agaran 543
        $cost += $icost;
544
#       %BomData{ById}{$id}{RefDes}
327 agaran 545
#       $bomtable->add(' '.$n.' ', $Inv[$id]{Description}.' ', $Inv[$id]{Manufacturer_Partno}.' ', $Inv[$id]{Manufacturer}.' ',
546
#               $Inv[$id]{Ordercode}.' ', ' '.$quant.' ', sprintf("%.3f ",$Inv[$id]{Price}), sprintf("%.3f ",$icost));
330 agaran 547
        $bomtable->add($bn++, $Inv[$id]{Description}, $Inv[$id]{Manufacturer_Partno}, $Inv[$id]{Manufacturer},
327 agaran 548
                $Inv[$id]{Ordercode},$quant, sprintf("%.3f",$Inv[$id]{Price}), sprintf("%.3f",$icost));
330 agaran 549
 
332 agaran 550
#       $parttable->add($pn++, $Inv[$id]{Description}, $Inv[$id]{Manufacturer_Partno}, $BomData{$id}{Footprint}, 
551
#               strbreak(join (', ', sort @{$BomData{$id}{RefDes}}),43), join("\n",@{$BomData{$id}{Files}}));
318 agaran 552
}
553
 
332 agaran 554
 
325 agaran 555
printf $out "file generated at %s\n\n", scalar localtime(time());
556
 
557
print $out $bomtable->rule('-','+');
558
print $out $bomtable->title();
559
print $out $bomtable->rule('-','+');
560
print $out $bomtable->body();
561
print $out $bomtable->rule('-','+');
562
 
330 agaran 563
printf $out "\nTotal cost: %.3f\n\n\n", $cost;
325 agaran 564
 
332 agaran 565
 
333 agaran 566
$pn = 1;
332 agaran 567
foreach my $fkey (sort keys %Files) {
568
        my $rowblock = 0;
569
        foreach my $id ( sort {my $p = $Inv[$a]{Manufacturer} cmp $Inv[$b]{Manufacturer}; if ($p == 0) {
570
                return $Inv[$a]{Description} cmp $Inv[$b]{Description}}; return $p; }  keys %{$Files{$fkey}}) {
571
 
334 agaran 572
                my @p = split(/\n/, strbreak(join (', ', sort @{$Files{$fkey}{$id}{RefDes}}),43));
573
                while (@p) {
574
                        $parttable->add($pn, $Inv[$id]{Description}, $Inv[$id]{Manufacturer_Partno}, $BomData{$id}{Footprint},
575
                                etrim(trim(shift @p)), $fkey);
576
                }
332 agaran 577
 
334 agaran 578
                $pn++;
332 agaran 579
        }
580
        $parttable->add('--','=========','==========','==========','==========','==========');
581
}
582
 
330 agaran 583
print $out $parttable->rule('-','+');
584
print $out $parttable->title();
585
print $out $parttable->rule('-','+');
586
print $out $parttable->body();
587
print $out $parttable->rule('-','+');
325 agaran 588
 
330 agaran 589
 
590
#close PARTMAP;
591
 
592
printf STDERR "\tFinished, output saved in %s\n", $Config{outfile};