APL (Dyalog), 60 5959 61 bytes
+2 since charges must be given signed.
Anonymous infix function. Takes list of ions (anion, cation) as left argument and list of corresponding charge magnitudescharges as right argument.
{∊(⍺{⍵∧1<≢⍺∩⎕D,⎕A:1⌽')(',⍺⋄⍺}¨m),¨(m←s≠1)/¨'_',∘⍕¨s←⍵÷⍨∧/⍵}∘| {…}∘| anonymous lambdafunction where ⍺ is left argument and ⍵ represent the left andis right argumentsargument's magnitude:
∧/⍵ LCM of the charges
⍵÷⍨ divide the charges by that
s← store in s (for subscripts)
'_',∘⍕¨ format (stringify) and prepend underbar to each
(…)/ replicate each letter of each with the corresponding value from:
s≠1 Is s different from 1? (gives 1 or 0)
m← store in m (for multiple)
(…),¨ prepend the following respectively to those:
⍺{…}¨m for each, call this function with ions and m as arguments:
⎕D,⎕A Digits followed by uppercase Alphabet
⍺∩ intersection of ion and that
≢ tally the number of characters in that
1< Is one less than that? (i.e. do we have a multi-element ion?)
⍵∧ and do we need multiple of that ion?
: if so, then:
')(',⍺ prepend the string to the ion
1⌽ cyclically rotate one step to the left (puts ) on the right)
⋄ else
⍺ return the ion unmodified
∊ ϵnlist (flatten)