As the Event Receiver works fine when triggered from the UI -> there's no problem with the ER registration itself.
The code that does the Update looks "OK" (not "perfect", a few improvements can be done, like calling
spwebs.AllowUnsafeUpdates = true only once, before the loop), and
spwebs.AllowUnsafeUpdates = false; is actually not needed since
spwebs is disposed right after the end of the loop. But, OK, no big problem there.
The first conclusion I can come to at the moment is: your ER fails in the case it's fired from the code. So, we need the code of the ER.
One thing I've seen quite often: you may assume, in the ER code, that there's a Web context (e.g. a call to
HTTPContext.Current)... but there's no Web context if your Update code is called from, let's say, a console app (or a job, or at speficic steps of a workflow...).
So, one more question (in addition to the ER code): where is your Update code called from? Console App? WinForm? Job? Workflow?...
OK, thanks to the comments below, I now have a clear picture of what happens: you actually disable event firing (
this.EventFiringEnabled = false). This piece of code disables all events on all lists. What it does internally is setting a flag on the thread to disable events. It means all events fired in the same thread are disabled for the life of the thread or till
this.EventFiringEnabled = true.