Merge branch 'master' of orgmode.org:worg
[worg.git] / org-contrib / org-bom.org
blob7622139b198b065e4c825e67d0b2ffbc59dd4f90
1 #+OPTIONS:    H:3 num:nil toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t TeX:t LaTeX:t skip:nil d:(HIDE) tags:not-in-toc
2 #+STARTUP:    align fold nodlcheck hidestars oddeven lognotestate
3 #+SEQ_TODO:   TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
4 #+TAGS:       Write(w) Update(u) Fix(f) Check(c) 
5 #+TITLE:      BOM : Bills-of-materials
6 #+AUTHOR:     Christian Fortin
7 #+EMAIL:      frozenlock AT gmail DOT com
8 #+LANGUAGE:   en
9 #+CATEGORY:   worg-tutorial
11 [[file:index.org][{Back to Worg's index}]]
14 * BOM introduction
16   This add-on collects components across the entire org buffer (even
17   in drawers), making it easy to retrieve and sort data. It uses the
18   column special name as a landmark. We will refer to them as
19   'Keywords'. The keywords are searched using a string-match function,
20   which gives the ability to have multiple column with the same
21   functionality, but also to use the column name as we would usually
22   with org-mode. For example, we can have 'tag' and 'tag2', both are
23   recognized by the BOM add-on and can be used in a spreadsheet-like
24   formula without any confusion. The keywords are also
25   case-insensitive. 'Component' and 'component' will work in the same
26   way.
28   The BOM is usually used with a dynamic block. (You can use the
29   different functions in emacs-lisp code, but this is beyond the
30   purpose of this tutorial.) Here is the basic dynamic block:
32 :  #+BEGIN: bom
33 :  #+END:
35   And here is what we obtain at this point:
36 :  #+BEGIN: bom
37 : | Section | Tag | Component | Quantity |
38 : |---------+-----+-----------+----------|
39 :  #+END:
41   The table is empty, because we have to either:
42   1. Add keywords in a table;
43   2. Add a line-component.
45 * BOM keywords
46 ** Component
48      This is the most important keyword and act as the trigger. For
49   this example, let's say we write down things we want to buy. In 
50   this case, a new keyboard for our computer.  This is how the
51   table should be:
52   
53 :  |   | Material  |
54 :  | ! | Component |
55 :  |---+-----------|
56 :  |   | Keyboard  |
57   
58   The '!' character is used in org table to specify column name, such
59   as our keyword, 'component'.
60   And here is what the bill-of-materials for this table is:
61      
62 :  #+BEGIN:  bom 
63 : | Section   | Tag | Component | Quantity |
64 : |-----------+-----+-----------+----------|
65 : | Component |     | Keyboard  |        1 |
66 :  #+END:
68   As you can see, the heading (Component) was automatically
69   used as the 'section', which doesn't require attention for
70   now. The quantity is, unsurprisingly, 1. There is nothing in the tag
71   column for now, so let's dismiss it by adding the parameter *:no-tag
72   t*. 
73   This will results in the following:
74 :  #+BEGIN: bom  :no-tag t
75 : | Section   | Component | Quantity |
76 : |-----------+-----------+----------|
77 : | Component | Keyboard  |        1 |
78 :  #+END: 
79   
80   Now suppose that our friend too wants a new keyboard.
82 :  |   | For    | Material  |
83 :  | ! |        | Component |
84 :  |---+--------+-----------|
85 :  |   | Me     | Keyboard  |
86 :  |   | Friend | Keyboard  |
87      
88 :   #+BEGIN: bom :no-tag t
89 :  | Section   | Component | Quantity |
90 :  |-----------+-----------+----------|
91 :  | Component | Keyboard  |        2 |
92 :   #+END:
94   As expected, we get 2 keyboards.
95      
96 ** Section
97    
98      The section is used to separate what would otherwise be an
99   identical component. Suppose we don't want our friend's wishes to be
100   in the same BOM as our, but still have them in the same table.
102 :  |   | For     | Material  |
103 :  | ! | Section | Component |
104 :  |---+---------+-----------|
105 :  |   | Me      | Keyboard  |
106 :  |   | Friend  | Keyboard  |
108   This will results in the following BOM:
110 :  #+BEGIN: bom :no-tag t
111 : | Section | Component | Quantity |
112 : |---------+-----------+----------|
113 : | Friend  | Keyboard  |        1 |
114 : | Me      | Keyboard  |        1 |
115 :  #+END:
117   Please note that when a component is given a section, it isn't
118   associated with the heading anymore. As an alternative, you can set
119   a ':SECTION:' property in the heading, which will be inherited by
120   all the components _without_ a specified section.
121   Section's priorities are as follow: 
123   1. Given section with the 'section' keyword;
124   2. The SECTION property;
125   3. The heading.
127 ** Qty
129      With this keyword, it is possible to specify a quantity for the
130   associated component. In our always improving scenario, we now want to
131   give a keyboard to another of our friend (as a gift). This is going to
132   be bought at the same time as our keyboard, so they belong together.
134 :  |   | For     | Material  |     |
135 :  | ! | Section | Component | Qty |
136 :  |---+---------+-----------+-----|
137 :  |   | Me      | Keyboard  |   2 |
138 :  |   | Friend  | Keyboard  |   1 |
140 :   #+BEGIN: bom :no-tag t
141 :  | Section | Component | Quantity |
142 :  |---------+-----------+----------|
143 :  | Friend  | Keyboard  |        1 |
144 :  | Me      | Keyboard  |        2 |
145 :   #+END:
146      
147   *Important*: If Qty keyword is present, then any empty field will
148   be considered as _zero_. This way, multiple column quantity are
149   made quite easily:
150      
151 :  |   | For     | Material  | Personal | Gift |
152 :  | ! | Section | Component |      Qty | Qty2 |
153 :  |---+---------+-----------+----------+------|
154 :  |   | Me      | Keyboard  |        1 | 1    |
155 :  |   | Friend  | Keyboard  |        1 |      |
157 :   #+BEGIN: bom :no-tag t
158 :  | Section | Component | Quantity |
159 :  |---------+-----------+----------|
160 :  | Friend  | Keyboard  |        1 |
161 :  | Me      | Keyboard  |        2 |
162 :   #+END:  
164 ** Tag
166      When a BOM starts to get big, we often need a quick reminder of
167   why we need certain component. Another use is also to identify the
168   component. As the Qty keyword, multiple Tag columns can be associated
169   with a single component. Here we will simply use the tag as a reminder
170   of what we want to look for in the store.
172 :  |   | For     | Material  | Personal | Gift | Need               |
173 :  | ! | Section | Component |      Qty | Qty2 | Tag                |
174 :  |---+---------+-----------+----------+------+--------------------|
175 :  |   | Me      | Keyboard  |        1 | 1    | Matching colors    |
176 :  |   | Friend  | Keyboard  |        1 |      | Dinosaurs pictures |
178   To show the tag column in the BOM, we simply remove the no-tag
179   parameter.
180 :  #+BEGIN: bom
181 : | Section | Tag                | Component | Quantity |
182 : |---------+--------------------+-----------+----------|
183 : | Friend  | Dinosaurs pictures | Keyboard  |        1 |
184 : | Me      | Matching colors    | Keyboard  |        2 |
185 :  #+END:  
186   
188   If two Tag columns are present for a single Component column, the
189   tags will be associated with this component, separated by a comma.
191 * Renaming BOM columns
192      
193      It is possible to rename the BOM columns with the following
194   parameters:
195   - col-name-component
196   - col-name-section
197   - col-name-quantity
198   - col-name-tag
199   - col-name-description
200   - col-name-price
202   This is how our renamed BOM would look like:
203      
204 :  #+BEGIN: bom :col-name-section For :col-name-tag Need :col-name-component Stuff :col-name-quantity Qty
205 : | For    | Need               | Stuff    | Qty |
206 : |--------+--------------------+----------+-----|
207 : | Friend | Dinosaurs pictures | Keyboard |   1 |
208 : | Me     | Matching colors    | Keyboard |   2 |
209 :  #+END:  
211 * Multiple component's column
213      There is two way to add components in a section. Either by adding
214   other rows with the same section's name, or by adding other
215   columns. Both have their uses and they should come to you quite
216   naturally. In our example, we want more stuff.
218 :  |   | For     | Material  | Personal | Gift | Need               | Stuff     | More stuff | Much more stuff | How many |
219 :  | ! | Section | Component |      Qty | Qty2 | Tag                | Component | Component  | Component       | Qty      |
220 :  |---+---------+-----------+----------+------+--------------------+-----------+------------+-----------------+----------|
221 :  |   | Me      | Keyboard  |        1 | 1    | Matching colors    | Mouse     | Headset    | USB flash drive | 23       |
222 :  |   | Friend  | Keyboard  |        1 |      | Dinosaurs pictures |           |            |                 |          |
223 :  |   | Friend  |           |          |      |                    |           |            | CDs             | 50       |
224 :  |   | Friend  | Mouse     |        1 |      |                    |           |            |                 |          |
225      
226   This is beginning to get interesting. Note that even if we can
227   name the additional columns 'Component2' or 'ComponentAAA',
228   there's no use to do it if no table-formula uses the column
229   names. 
231 * Precise section selection
232   Now suppose we want to get OUR to-buy list. Simply specify
233   the section's parameter *:section Me*:
235 :   #+BEGIN: bom :section Me
236 :  | Tag             | Component       | Quantity |
237 :  |-----------------+-----------------+----------|
238 :  |                 | Headset         |        1 |
239 :  | Matching colors | Keyboard        |        2 |
240 :  |                 | Mouse           |        1 |
241 :  |                 | USB flash drive |       23 |
242 :   #+END:  
243      
244   Wait, where's the section column?  Well we don't need it anymore,
245   as we specified one.
247   A '+' sign will specify we want more than a single section. *:section
248   Me+Friend* will select both section, and add the quantity and tags
249   for each component. 
251 :  #+BEGIN: bom :section Me+Friend
252 : | Tag                                 | Component       | Quantity |
253 : |-------------------------------------+-----------------+----------|
254 : |                                     | CDs             |       50 |
255 : |                                     | Headset         |        1 |
256 : | Dinosaurs pictures, Matching colors | Keyboard        |        3 |
257 : |                                     | Mouse           |        2 |
258 : |                                     | USB flash drive |       23 |
259 :  #+END:
261   *Do not* put a whitespace between the section name and the '+' sign.
262   Speaking of whitespace, if you need one in a section name, simply
263   put it in a string: 
264 : #+BEGIN: bom :section "Section with whitespace"
266   We can also return every section that matches at least what we
267   provide. To activate this, use *:part-match t*. With this, if we
268   write "fr", the Friend section is returned. If we had another
269   section named "Frosting", than Friend and Frosting would have been
270   merged and we would have a total for both section.
272 :  #+BEGIN: bom :section fr :part-match t
273 : | Tag                | Component | Quantity |
274 : |--------------------+-----------+----------|
275 : |                    | CDs       |       50 |
276 : | Dinosaurs pictures | Keyboard  |        1 |
277 : |                    | Mouse     |        1 |
278 :  #+END:
280   It is also possible to specify that we don't want any section
281   containing "fr". For this, use the parameter *:remove t*.
283 :  #+BEGIN: bom :section fr :part-match t :remove t
284 : | Tag             | Component       | Quantity |
285 : |-----------------+-----------------+----------|
286 : |                 | Headset         |        1 |
287 : | Matching colors | Keyboard        |        2 |
288 : |                 | Mouse           |        1 |
289 : |                 | USB flash drive |       23 |
290 :  #+END:
292   In this case, getting all sections not containing "fr" is the
293   equivalent of choosing the section "Me".
295   If you simply want the components from the current heading, use the
296   parameter *:local-only t*. This will return components with the
297   current heading as their section, which is the default of every
298   component if no section is provided. If a section has been provided to
299   a component (and is not exactly equal to the heading), the component
300   will not be returned.
302   Here, we don't have any component under this heading:
303 :  #+BEGIN: bom :local-only t
304 : | Tag | Component | Quantity |
305 : |-----+-----------+----------|
306 :  #+END:
308 * BOM total
309   This is all really interesting, but when we're in a shop, we want
310   to know how many of each item we have to buy, we need a *total*.
311   For this, simply add the *:total t* parameter. We will also remove
312   the tags once again by using *:no-tag t*.
314 :  #+BEGIN: bom :total t :no-tag t
315 : | Component       | Quantity |
316 : |-----------------+----------|
317 : | CDs             |       50 |
318 : | Headset         |        1 |
319 : | Keyboard        |        3 |
320 : | Mouse           |        2 |
321 : | USB flash drive |       23 |
322 :  #+END:
324   This is the equivalent of merging every sections together.
325 * Adding a component without a table
327   There is another option you might need. If you ever want to
328   add a component without a table, use the #+BOM commentary. As any
329   other org-mode commentary, this one won't appear when exported to
330   another document (pdf, html, docbook..). It will, however, enable
331   you to add a single component in the bill-of-materials. Here is an
332   example:
333 :  #+BOM: Keyboard :section Need :tag "Matching colors"
335   As with the table components, you can simply give a component name if
336   you desire. If no section is given, it will be inherited as an
337   ordinary component in a table: a section property or the current
338   heading.
340 * Adding details
341   There is two way to add details to a BOM. The first one is to setq
342   `org-bom-details' with a plist containing, depending on your
343   needs, :description, :datasheet-pdf and :price. You must, however, at
344   least have the component name in the :name property. Here is an
345   example on how to set this variable:
347 #+BEGIN_SRC emacs-lisp
348 (setq org-bom-details '((:name "Keyboard" :description
349                           "Something" :price "40") 
350                           (:name "CDs" :description "Not
351                           DVDs" :datasheet-pdf "CD.pdf")))
352 #+END_SRC
353   Please note that the price is a *string*.
355   The other method, valid for the current buffer only, is to give one
356   or more bom-details table. It is recognized when a table is named as
357   such:
358 :  #+TBLNAME: bom-details
360   Once again, the column names are used. Contrary to the normal BOM
361   keywords however, these are case-sensitive and must be written
362   exactly as their property name. For example, the column of the
363   property ':name' must be 'name'.
364 :  #+TBLNAME: bom-details
365 : | ! | name     | description  | price |
366 : |---+----------+--------------+-------|
367 : |   | Keyboard | Used to type |    40 |
368 : |   | CDs      |              |       |
369   
370   Any bom-details table will temporarily overshadow the
371   `org-bom-details' variable, but won't erase or modify it. This means
372   you can safely use a bom-details table if you need to change some
373   local buffer description, while using `org-bom-details' in multiple
374   buffer.
376   Look at the CDs description. When a field is empty, it is *not* used
377   and BOM falls back to the property in the `org-bom-details'
378   variable.
379   
380 ** Description
381    
382    You can add a description column in a BOM by adding the
383    *:description t* parameter. 
385 :   #+BEGIN: bom :total t :no-tag t :description t
386 :  | Component       | Quantity | Description  |
387 :  |-----------------+----------+--------------|
388 :  | CDs             |       50 | Not DVDs     |
389 :  | Headset         |        1 | N/A          |
390 :  | Keyboard        |        3 | Used to type |
391 :  | Mouse           |        2 | N/A          |
392 :  | USB flash drive |       23 | N/A          |
393 :   #+END:
395    See how the CDs' description wasn't the empty field from the
396    bom-details table.
398 ** Price
399    
400    You can add a price column in a BOM by adding the *:price t*
401    parameter.
403 :   #+BEGIN: bom :total t :no-tag t :description t :price t
404 :  | Component       | Quantity | Price | Description  |
405 :  |-----------------+----------+-------+--------------|
406 :  | CDs             |       50 |       | Not DVDs     |
407 :  | Headset         |        1 |       | N/A          |
408 :  | Keyboard        |        3 |   120 | Used to type |
409 :  | Mouse           |        2 |       | N/A          |
410 :  | USB flash drive |       23 |       | N/A          |
411 :  |-----------------+----------+-------+--------------|
412 :  | TOTAL:          |          |   120 |              |
413 :      #+TBLFM: @>$3=vsum(@I..@>>)
414 :   #+END:
415    The price is automatically multiplied by the quantity of each
416    component. In addition, a total row is added at the table's bottom
417    with a vertical sum formula.
418    
419 ** Datasheet
420    
421    This is a special property and must be used only if you intend to
422    export in a pdf document. See [[LaTeX mode and bom-datasheet]] for more details.
423    
424 * List of BOM parameters
425   Here is a list of all the parameters usable in a BOM dynamic block,
426   as seen throughout this tutorial:
428   - no-tag :: Remove the tags column
429   - section :: Select this section (or more if there's a + sign)
430   - part-match :: Select every section with at least the string
431                   provided for the section parameter
432   - remove :: Select every sections except the one(s) provided
433   - descripton :: Add the description column
434   - price :: Add the price column and a total row at the bottom of the
435              table
436   - col-name-*** :: Rename the associated column
437 * Advanced and elisp functions
438 ** Speed up updates
439    Each BOM dynamic block scans the entire buffer individually. While
440    it is necessary that each block be able to update itself, it
441    becomes a waste when the command `org-update-all-dblocks' is
442    used. (The components usually aren't changing from a dblock evaluation to
443    another.)
444    
445    In order to speed up updates, there's a variable that can be used
446    to stop each BOM dblock from doing a buffer-wide scan. To disable the
447    scans, set `org-bom-update-enable' to nil.
449    The author uses a function similar to this one to speed up updates:
450 #+BEGIN_SRC emacs-lisp :exports code
451 (defun reg-update-project (&optional latex-mode)
452   "Update every table and dynamic block in the buffer. If latex-mode
453 is non-nil, various latex commands will be inserted."
454   (interactive)
455   (org-table-iterate-buffer-tables)
456   (org-bom-total); manually update the BOM database
457   (let ((org-bom-update-enable nil)
458         (org-bom-latex-mode latex-mode)
459         (org-bom-details (copy-tree org-bom-details)));so we don't overwrite
460     (org-bom-check-for-details-table); manually update `org-bom-details'
461     (org-update-all-dblocks))
462   (message "Project updated"))
463 #+END_SRC
464      
465 ** LaTeX mode and bom-datasheet
466   This mode isn't fully integrated to org-mode and should be seen as a
467   hack. It is however useful to the author, which is why it is
468   explained here.
470   Set the `org-bom-latex-mode' variable to non-nil in order to
471   activate the latex-mode. If set, all BOM dynamic block will insert
472   some latex commands.
474   These commands targets:
475   - Tags :: When there is more tags than `org-bom-latex-max-tags' per
476             component, the remaining tags are put in a pdf comment.
477   - Component name :: If a datasheet exists for the component, its
478                       name will become a link to its datasheet.
480                      
481   If you ever activate the LaTeX mode, use the bom-datasheet dynamic
482   block at the end of your document. The optional parameter
483   *:description t* will add a summary of all the components used in
484   this buffer with their description, just before the datasheets.
486 :  #+BEGIN: bom-datasheet
487 :  
488 :  #+LaTeX: \includepdf[pages=-,landscape=true,addtotoc={1, subsection, 1, CDs,CD.pdf}]{\DATASHEETPATH/CD.pdf}
489 :  
490 :  #+END:
492   As you may have noticed, there's a LaTeX variable in this command:
493         \DATASHEETPATH. In order to work, you must set this variable
494         using:
496 :       #+LATEX_HEADER: \newcommand{\DATASHEETPATH}{Name-of-the-folder/}'
498         Name-of-the-folder is the folder where the datasheets' files
499         are located.